API Compatible with Stripe SDK

Migrate from Stripe
Without Changing Code

Backend: Keep your existing Stripe SDK by overriding the API base URL and using your PayGlobe secret key (sk_*). Frontend: Replace Stripe.js script source with our compatible adapter - your payment logic stays unchanged.

Same endpoints and JSON format as Stripe. Amounts use minor units (cents). Webhook signing uses X-PayGlobe-Signature (HMAC-SHA256) verified against the raw request body.

// Configuration changes only!
import Stripe from 'stripe';
const stripe = new Stripe('sk_test_PayGlobe...', { // ← Change key
  apiVersion: '2023-10-16',
  host: 'api.payglobe.it', // ← Change host
  protocol: 'https',
  port: 443,
  basePath: '/mandala/v1' // ← Change basePath
});

// Your business logic stays the same! ✓
const paymentIntent = await stripe.paymentIntents.create({
  amount: 5000, // minor units (cents)
  currency: 'eur',
  description: 'Order #12345'
});

Why "Mandala" Gateway?

The word Mandala comes from Sanskrit, meaning "circle" or "center". In Hindu and Buddhist traditions, a mandala is a spiritual symbol representing the universe in harmony.

In our architecture, Mandala Gateway is the central hub that harmonizes external payment systems (Stripe ecosystem) with internal infrastructure (PayGlobe).

Central Hub

Single point of integration for Stripe-compatible merchants

Orchestration Layer

Translates Stripe API calls to PayGlobe protocol

Unified Center

Bridges multiple payment flows with a single API

Zero Refactoring

Drop-in replacement for Stripe SDK

"So, Mandala Gateway is not just a connector — it's a circle of integration. ⭕"

Stripe-Compatible API - Minimal Changes

Same Stripe.js syntax you already know

Backend

Just swap the script source and API keys

Keep using official Stripe SDK. Just point it to Mandala with your PayGlobe secret key (sk_*):

// Node.js
const stripe = new Stripe(
  process.env.PAYGLOBE_SECRET_KEY,
  {
    host: 'api.payglobe.it',
    basePath: '/mandala/v1'
  }
);

Frontend

Your payment logic stays unchanged

Load our Stripe-compatible adapter (configuration change):

<!-- Load Mandala Stripe-compatible adapter (our library) -->
<script src="https://api.payglobe.it/mandala/js/payglobe.js"></script>
<script src="https://api.payglobe.it/mandala/js/payglobe-stripe-adapter.js"></script>

Then use Stripe-like syntax (our adapter, not Stripe SDK):

  • Use Stripe() constructor (provided by our payglobe-stripe-adapter.js)
  • Create Payment Element → shows processor iframe
  • Call stripe.confirmPayment(...) as before
Note: Payment form renders hosted page (Worldline/Shift4/Nexi/Stripe*) in iframe (PCI-compliant, no code changes needed).

Webhooks

Only change signature verification

Endpoint stays the same. Signature: header X-PayGlobe-Signature (HMAC-SHA256):

const sig = req.header('x-payglobe-signature');
const digest = crypto.createHmac('sha256', secret)
  .update(req.body).digest('hex');
if (sig !== digest) return res.sendStatus(400);

Tip: Accept both Stripe-Signature and X-PayGlobe-Signature during rollback.

Compatibility Guarantee: Amounts in cents, Idempotency-Key, Stripe-Version, same ID prefixes (pi_, cs_), states, and errors (card_declined, etc.) — all identical to Stripe*.

Key Features

Everything you need to migrate from Stripe*

Stripe*-Compatible API

Stripe*-compatible endpoints and JSON responses. Keep your existing Stripe* integration by overriding the API base and using your PayGlobe secret key (sk_*) on the server. Amounts use minor units (cents). Webhook signing uses X-PayGlobe-Signature (HMAC-SHA256) verified against the raw request body.

Idempotency Support

Use Idempotency-Key header to prevent duplicate payments. 24-hour TTL ensures reliability.

Rate Limiting

Built-in rate limiting with Bucket4j. 100 requests/minute default, configurable per merchant.

Enterprise Data Persistence

Reliable database storage with complete transaction history and real-time status tracking.

Status Mapping

Automatic PayGlobe → Stripe* status conversion. APPROVEDsucceeded, etc.

Webhook Support

Stripe-compatible webhooks with Stripe-Signature header (HMAC-SHA256). Robust retry policy: 5 attempts with exponential backoff (5s, 25s, 125s, 625s, 3125s). Events: payment_intent.succeeded, payment_intent.payment_failed, charge.refunded.

How It Works

Mandala orchestrates all payment flows seamlessly

Merchant Backend
Stripe* SDK / curl with sk_test_*
PayGlobe Infrastructure
Mandala Gateway
Stripe*-Compatible API
{"amount": 5000, "currency": "eur"}
(amount in cents)
PayGlobe Gateway
Internal Processing
{"amount": 50.00, "currency": "EUR"}
Payment Processors
Shift4 / Worldline / Stripe* / Voucher Meal
Real payment processing

API Compatibility

Stripe* endpoints fully supported

Endpoint Method Status Description
/v1/payment_intents POST ✓ Supported Create payment intent
/v1/payment_intents/:id GET ✓ Supported Retrieve payment intent
/v1/payment_intents/:id/confirm POST ✓ Supported Confirm payment intent
/v1/payment_intents/:id/cancel POST ✓ Supported Cancel payment intent
/v1/refunds POST ✓ Supported Create refund
/v1/webhook_endpoints POST GET ✓ Supported Register webhook endpoint (WooCommerce compatible)

Migration in 3 Steps

Switch from Stripe to PayGlobe without changing your payment logic

1

Update Backend SDK

Configure Stripe SDK to point to Mandala instead of api.stripe.com.

// Backend (Node.js)
import Stripe from 'stripe';
const stripe = new Stripe(
  'sk_test_PayGlobe...',
  {
    host: 'api.payglobe.it',
    basePath: '/mandala/v1'
  }
);
2

Update Frontend Scripts

Replace Stripe.js with our Stripe-compatible adapter.

<!-- Before -->
<script src="https://js.stripe.com/v3/"></script>

<!-- After -->
<script src="https://api.payglobe.it/mandala/js/payglobe.js"></script>
<script src="https://api.payglobe.it/mandala/js/payglobe-stripe-adapter.js"></script>
// Update API key
const stripe = Stripe('pk_test_PayGlobe...');
3

Test & Deploy

Your payment logic stays the same. Run tests and deploy!

// Same Stripe API calls
const intent = await stripe
  .paymentIntents.create(...);

// Different backend!

Get your API keys from Merchant Portal

Try it Live!

Test both integration methods side-by-side

Direct API

Use native PayGlobe/Mandala APIs with fetch()

Official Mandala API
Direct REST Calls
Uses: fetch() to call POST /v1/payment_intentsPOST /v1/payment_intents/:id/confirm
Response includes next_action.redirect_to_url.url for iframe
Error

SDK Adapter
SDK Compatible

Use familiar Stripe.js API (loads processor iframe)

SDK Adapter (Stripe-like API)
Not Official Stripe SDK
Uses: payglobe-stripe-adapter.js (our library with Stripe-like syntax)
Calls same Mandala APIs internally, familiar syntax for Stripe users
Same result as Direct API, easier migration path
Error

Using test credentials: pk_test_PayGlobe123456789TestPublishable

100% SDK Compatible

Test with Official Stripe SDK

Run a real compatibility test using the official Stripe Node.js client. This proves Mandala works as a true drop-in replacement.

What This Tests:
  • Create PaymentIntent with official SDK
  • Retrieve PaymentIntent
  • Confirm PaymentIntent
  • Cancel PaymentIntent
View SDK Docs

$ npm install stripe

$ node test-stripe-client.js

Click "Run SDK Test" to start...

Secret Key:
Uses Stripe SDK v14 • Real API calls • Enter your sk_test key above

Quick Start Example

Create a payment in seconds

cURL (Server-Side)

Amount is in minor units (cents), like Stripe.

curl -X POST https://api.payglobe.it/mandala/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: {{uuid}}" \
  -d '{
    "amount": 5000,
    "currency": "eur",
    "description": "Order #12345"
  }'

Response (JSON)

{
  "id": "pi_test_abc123...",
  "object": "payment_intent",
  "amount": 5000, // in cents
  "currency": "eur",
  "status": "requires_payment_method",
  "client_secret": "pi_test_abc123_secret_xyz",
  "created": 1730995200
}

Note: Initial status is requires_payment_method, matching Stripe behavior.

Complete Payment Flow

Full integration with return URL for post-payment redirect

1. Server-Side: Create PaymentIntent
// POST /v1/payment_intents
const response = await fetch('https://api.payglobe.it/mandala/v1/payment_intents', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_test_...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    amount: 5000, // €50.00 in cents
    currency: 'eur',
    description: 'Order #12345'
  })
});
const { id, client_secret } = await response.json();
// Return client_secret to frontend
2. Client-Side: Confirm Payment with return_url
// Load our Stripe-compatible adapter
<script src="https://api.payglobe.it/mandala/js/v3/payglobe-stripe-adapter.js"></script>

// Initialize with your publishable key
const stripe = Stripe('pk_test_...');

// Confirm payment - user will be redirected to payment page
const result = await stripe.confirmPayment({
  clientSecret: 'pi_test_xxx_secret_yyy', // from server
  confirmParams: {
    // IMPORTANT: URL where user returns after payment
    return_url: 'https://your-site.com/payment-complete?order_id=123'
  }
});
⚠️ Important: The return_url is mandatory! After payment, the user will be redirected to this URL with these query parameters:
  • payment_intent - PaymentIntent ID
  • payment_intent_client_secret - Client secret
  • redirect_status - succeeded, failed, or canceled
3. Handle Return: Verify Payment Status
// On your return_url page (e.g., /payment-complete)
const urlParams = new URLSearchParams(window.location.search);
const paymentIntentId = urlParams.get('payment_intent');
const redirectStatus = urlParams.get('redirect_status');

if (redirectStatus === 'succeeded') {
  // Payment successful! Verify on server-side
  const response = await fetch(`/api/verify-payment?pi=${paymentIntentId}`);
  const { verified } = await response.json();
  
  if (verified) {
    showSuccessMessage();
  }
} else {
  // Payment failed or canceled
  showErrorMessage();
}
4. Server-Side: Verify Payment (Optional but Recommended)
// GET /v1/payment_intents/:id
const response = await fetch(`https://api.payglobe.it/mandala/v1/payment_intents/${paymentIntentId}`, {
  headers: {
    'Authorization': 'Bearer sk_test_...'
  }
});
const paymentIntent = await response.json();

if (paymentIntent.status === 'succeeded') {
  // Payment confirmed - fulfill the order
  fulfillOrder(paymentIntent.metadata.order_id);
}
💡 Tip: Always verify the payment status server-side before fulfilling orders. The redirect_status query parameter can be manipulated by users.
✅ Example return_url Result (Success)
https://your-site.com/payment-complete?order_id=123
&payment_intent=pi_test_abc123
&payment_intent_client_secret=pi_test_abc123_secret_xyz
&redirect_status=succeeded
❌ Example return_url Result (Failed)
https://your-site.com/payment-complete?order_id=123
&payment_intent=pi_test_abc123
&payment_intent_client_secret=pi_test_abc123_secret_xyz
&redirect_status=failed