Skip to main content

Order Fulfillment

After a user completes a payment, your system needs to receive a confirmation signal and deliver the goods or services (i.e., "Fulfillment").

Due to the asynchronous nature of blockchain transactions, frontend redirects cannot serve as the final basis for payment success. Webhook is the only authoritative way to confirm payment status and perform order fulfillment.

Fulfillment Flow Overview

GStable settlement speed is extremely fast (usually within 30-40 seconds), so we recommend adopting the following standard fulfillment logic:

  1. Listen to Webhook: Configure an endpoint on your server to receive POST requests.
  2. Verify Signature: Ensure the request comes from GStable to prevent forgery.
  3. Check Status: Decide timing of fulfillment based on event type (paid or completed).
  4. Execute Business Logic: Update order status in the database, send email, or issue entitlements.

Core Event States

In the Checkout flow, you primarily need to focus on session related lifecycle events.

1. Payment Success (session.paid)

  • Trigger Timing: User has completed the transfer on-chain, and the transaction has been confirmed by the blockchain.
  • Fund Status: Funds have been transferred out of the user's wallet and are undergoing cross-chain routing or consolidation.
  • Recommended Action:
    • Lock Inventory: Prevent overselling.
    • Instant Delivery: For low-risk digital goods (such as game items, e-books), fulfillment can be done at this stage without waiting for final fund arrival.

2. Settlement Completed (session.completed)

  • Trigger Timing: Funds have successfully reached the merchant's configured collection wallet address.
  • Fund Status: Secure in Pocket. The transaction process is completely finished.
  • Recommended Action:
    • High-Value Fulfillment: For shipping physical goods, large amount top-ups, etc., it is recommended to wait for this event.
    • Financial Reconciliation: Use this as the basis for revenue recognition.
Settlement Speed

Thanks to our non-custodial full-chain payment architecture, it typically takes only 30 to 40 seconds from session.paid to session.completed.


Webhook Data Structure

When an event triggers, your server will receive a POST request containing the following JSON structure.

The data structure in Payload is completely consistent with the data returned by the Query Payment Session interface.

Example: Settlement Completed Event (session.completed)

{
"eventId": "evt_i4NWz4J3QkWugyq1",
"eventType": "session.completed",
"businessType": "session",
"occurrence": "2026-01-04 02:52:38",
"isSubscribable": true,
"payload": {
// --- Session Details (Corresponds to session object in API response) ---
"sessionData": {
"sessionId": "sess_example_payment_01",
"sessionType": "chk",
"businessId": "",
"businessType": "",
"clientCode": "clt_l8ZBnpOSV9pVlaLK0dP5jsq5NzfqN3Vc",
"payer": "0xUserWalletAddress...", // Payer Wallet Address
"payerEmail": "user@example.com",
"payerEmailRequired": 1,
"paidToken": "0xTokenAddress...", // Token contract actually paid by user
"receiptId": "rcpt_txUdSs2ASditQhrr",
"recipient": "0xMerchantWalletAddress...",
"settlementToken": "polygon::usdc",
"feeModel": 1,
"amount": 15000000, // Order Amount (Micro-USD)
"lineItems": {
"items": [
{
"lineItemId": "li_example_item_03",
"itemType": "product",
"quantity": 1,
"unitPrice": 10000000,
"productData": { "productName": "Premium Membership" }
}
// ...
]
},
"expirationTime": 1767343946,
"successUrl": "https://yoursite.com/order/success",
"paymentUrl": "https://pay.gstable.io/checkout/sess_...",
"signedOnce": 1,
"initiateTx": "0xSourceTxHash...",
"completeTx": "0xTargetTxHash...",
"paymentSucceededTime": "2025-12-25 07:26:35",
"settlementSucceededTime": "2025-12-25 07:27:10",
"createAt": "2026-01-02 07:52:26",
"status": "completed",
"metadata": {
"internalOrderId": "ord_8823_xyz"
}
},
// --- Transaction Details (Corresponds to transaction object in API response) ---
"transaction": {
"orderId": "0x8a7f...",
"channelId": "da1e3483",
"orderType": 7106155,
"from": "0xUserWalletAddress...",
"to": "0xPlatformContractAddress...",
"metaData": "0x",
"totalValue": 15000000, // User Payment Total
"paymentValue": 15000000, // Order Face Value
"feeValue": 30000, // Fee (Micro-USD)
"netValue": 14970000, // Merchant Net Receipt (Micro-USD)
"sourceTx": "0xSourceTxHash...",
"targetTx": "0xTargetTxHash...",
"executeTime": "2025-12-25 07:26:35",
"finalizeTime": "2025-12-25 07:26:35"
}
}
}

Key Field Analysis

  • eventId: Global unique identifier for the event. Recommended for idempotency handling (i.e., if the same eventId is received, ignore processing to prevent duplicate shipping).
  • payload.sessionData.metadata: Custom data you passed when creating the Session. This is the key to associating the Webhook notification with the order in your local database (internalOrderId).
  • payload.transaction.netValue: Merchant actual received amount (Micro-USD). Please use this field for financial reconciliation.

Security & Handling

To ensure fund security, you must verify the signature of all Webhook requests on your server.

1. Verify Signature

GStable will include x-gstable-signature in the request Header. You need to use the Signing Secret obtained from the Dashboard to calculate the HMAC-SHA256 signature and compare it.

2. Idempotency Handling

Network fluctuations may cause Webhook retries. Be sure to record processed eventIds.

Pseudo-code Example: Handling Fulfillment
app.post('/webhook', (req, res) => {
// 1. Verify Signature (Refer to Webhook Development Docs)
verifySignature(req);

const event = req.body;

// 2. Idempotency Check
if (isEventProcessed(event.eventId)) {
return res.send({ received: true });
}

// 3. Process Business Logic
if (event.eventType === 'session.completed') {
const session = event.payload.sessionData;
const transaction = event.payload.transaction;

const orderId = session.metadata.internalOrderId;
const receivedAmount = transaction.netValue; // Actual received amount

// Execute Fulfillment: Update Database, Ship
fulfillOrder(orderId, receivedAmount);
}

// 4. Record Event Processed
recordEvent(event.eventId);

res.send({ received: true });
});
Development Guide

For detailed verification code implementation (Node.js, Python, Go, etc.) and the complete event type list, please refer to the dedicated Webhook development documentation.


Summary

You have now completed the Checkout integration!

  1. You created a payment session via API.
  2. The user completed payment on the hosted page.
  3. Your server confirmed receipt and completed order fulfillment via Webhook.

Now, you can go to the Dashboard to view transaction data, or continue to explore more advanced features.