Order fulfillment

Confirm payment results after Checkout succeeds and then fulfill the order.
Payment results must come from webhook notifications or a verified Order Query result. returnUrl is only for display, status re-checks, and guiding the customer to the next step.

Order fulfillment after successful payment

After a customer completes payment through Onerway Checkout, you still need to deliver the product, service, or access they purchased. This delivery step is called fulfillment.

Common fulfillment scenarios include:

  • Digital delivery: Grant membership access, issue license keys, or provision SaaS accounts
  • Physical delivery: Trigger shipment through your warehouse or order management system
  • Transaction records: Store order details and product information for reconciliation, risk control, and support

Fulfillment approaches

There are two main ways to handle fulfillment:

  • Manual fulfillment: Review payment notifications in the dashboard and complete fulfillment manually
  • Automatic fulfillment: Build an automated workflow that is triggered by webhooks

Manual fulfillment works well for low-volume or pilot operations. For workflows that need stable scaling, automatic fulfillment is recommended.

Automatic fulfillment

Automatic fulfillment is typically built on a dual-trigger mechanism: one trigger comes from the webhook (server-to-server asynchronous notification), and the other comes from the landing-page status check (a re-check after the customer returns to your page):

  • Webhook is the primary fulfillment trigger. If your endpoint does not respond correctly, Onerway retries the notification
  • Landing-page re-check helps confirm status after the customer returns and improves the experience when webhooks are slightly delayed

Implement an idempotent fulfillment function

Whether fulfillment is triggered by a webhook or a landing-page re-check, your server should call the same fulfillment function. In this document, we use fulfillCheckout as an example name.

The key requirement is simple: the same order must only be fulfilled once. Because of retries, duplicate webhook deliveries, or concurrent requests, the fulfillment function must be idempotent.

Function requirements

fulfillCheckout should at least:

  1. Deduplicate by merchantTxnId
  2. Confirm the transaction is successful through Order Query or a verified webhook result
  3. Execute the actual fulfillment steps
  4. Persist the fulfillment result for auditing and later lookup
  5. Keep fulfillment logs for troubleshooting

Example

async function fulfillCheckout(merchantTxnId, verifiedTxn = null) {
  return db.transaction(async (tx) => {
    const existing = await tx.fulfillments.findByMerchantTxnId(merchantTxnId);
    if (existing) {
      return existing;
    }

    const txn = verifiedTxn ?? await queryOrderByMerchantTxnId(merchantTxnId);
    if (!txn || txn.txnType !== 'SALE' || txn.status !== 'S') {
      return { success: false, reason: 'Payment not completed' };
    }

    await provisionService(txn);

    const record = await tx.fulfillments.create({
      merchantTxnId,
      status: 'fulfilled',
      fulfilledAt: new Date()
    });

    return { success: true, record };
  });
}

If your webhook handler has already confirmed a successful payment, pass that result directly as verifiedTxn. Otherwise, query the order first and then decide whether to fulfill.

Fulfillment operations

The exact implementation of fulfillCheckout depends on your business, but common fulfillment actions include:

  • Grant access to digital content or services
  • Trigger shipment through your warehouse system
  • Save transaction and order details to your database
  • Send an order confirmation email to the customer
  • Update inventory data
  • Record accounting and reconciliation data
For digital services, the landing page can perform an immediate status check first. The actual fulfillment action should still be driven by the webhook.

Create a webhook handler

The webhook handler receives payment events and calls fulfillCheckout when the payment is successful.

When a new payment transaction is created, Onerway sends a webhook to the URL you configured. Set up a dedicated endpoint on your server to receive, process, and acknowledge these events.

Webhook event types

For fulfillment, the relevant events are:

  • notifyType = TXN + txnType = SALE + status = S: Payment succeeded and should be fulfilled
  • notifyType = TXN + txnType = SALE + status = F: Payment failed or was declined and should not be fulfilled

Status values:

  • S = Success, fulfill the order
  • F = Failed/Declined/Rejected, do not fulfill

See Transaction NotificationPayments API for the full event structure.

Minimal implementation

app.post('/webhook', async (req, res) => {
  const payload = req.body;

  if (!verifySignature(payload, process.env.MERCHANT_SECRET_KEY)) {
    return res.status(401).send('Invalid signature');
  }

  res.status(200).send(payload.transactionId);

  if (payload.notifyType === 'TXN' &&
      payload.txnType === 'SALE' &&
      payload.status === 'S') {
    setImmediate(async () => {
      try {
        await fulfillCheckout(payload.merchantTxnId, payload);
      } catch (error) {
        console.error(`Fulfillment error: ${error.message}`);
      }
    });
  }
});

Webhook best practices

  • Verify first: Always verify webhook authenticity before processing
  • Respond quickly: Return an acknowledgment immediately to avoid retries
  • Process asynchronously: Put fulfillment into a background job so HTTP responses do not block
  • Keep it idempotent: Use merchantTxnId as the deduplication key for fulfillment
  • Only handle successful states: Failed or pending states should be recorded, not fulfilled

Delayed payment methods

Some payment methods, such as bank transfers and convenience store payments, use asynchronous settlement. Finishing the checkout page does not mean the funds have fully settled yet.

Common delayed payment methods:

Payment MethodTypeTypical Delay
TrustlyBank transfer2-5 days
KonbiniConvenience store paymentUp to 14 days
OXXOConvenience store paymentUp to 14 days

For these methods, transactions may remain in Pending for some time. Only fulfill after webhook or order query clearly returns status = S.

Fulfillment rule:

  • Wait until status = S before fulfilling
  • Do not fulfill when status = P or status = F
Do not provide services just because the page redirected successfully. Customers may close the page during the redirect, or the payment may still be processing. Always require a clear success state from webhook or Order Query before you fulfill.