Return URLs

Use merchantUrl to tell the hosted checkout where to send the customer when they finish. It is optional and works the same on every order-creating call: POST /api/v1/pay/session/create, POST /api/v1/pay/pos/create, POST /api/v1/pay/qr/create and POST /api/v1/merchant/checkout-links.

"merchantUrl": {
  "success": "https://shop.example/qasimha/return?order=10001",
  "failure": "https://shop.example/qasimha/return?order=10001",
  "cancel": "https://shop.example/cart"
}
FieldWhen the customer is sent thereTypical value
successThe order is confirmedYour return page
failureThe order was declined, expired, or failedThe same return page
cancelThe customer pressed "Cancel and return to store" before payingYour cart
notificationReserved. Don't send it—

Rules

  • Each URL must be absolute and at most 1,024 characters, with no #fragment.
  • Allowed schemes: https; http only when the host is localhost or 127.0.0.1; or a custom app scheme such as myshop:// for mobile apps. A custom scheme must be at least 3 characters, and javascript, data, file, blob and vbscript are refused.
  • success and failure must be sent together. Sending only one, or only cancel, is refused.
  • cancel defaults to failure if you leave it out.
  • notification is reserved and refused. Use webhooks instead.
  • You can keep your own query string, such as ?order=10001. Qasimha adds its parameters to it.

A URL that breaks a rule returns 400 VALIDATION_ERROR, and errors names the field and the rule:

{
  "success": false,
  "code": "VALIDATION_ERROR",
  "message": "One or more validation errors occurred.",
  "data": null,
  "errors": [
    { "field": "MerchantUrl.Success", "message": "must use https, http on localhost, or a custom app scheme." }
  ]
}

What Qasimha adds to the URL

At the end of the journey the hosted page sends the customer to your URL with these parameters appended (? or & is chosen for you):

ParameterValue
orderIdThe Qasimha order id (UUID)
orderReferenceIdYour reference, URL-encoded
statusconfirmed on the success URL; declined, expired, or failed on the failure URL; cancelled on the cancel URL

For example:

https://shop.example/qasimha/return?order=10001&orderId=9a2e6c4b-1111-4c2d-8a1f-000000000001&orderReferenceId=SHOP-ORDER-10001&status=confirmed

What the customer sees:

  • Success, decline and expiry screens show a "Return to store" button and redirect on their own after 5 seconds.
  • "Cancel and return to store" is shown on every step before payment. It sends the customer to cancel. The order stays AwaitingCheckout until it expires after two hours, or until you cancel it with POST /api/v1/merchant/orders/{id}/cancel.

A return is not proof of payment. The return isn't signed, and anyone can open your return URL with status=confirmed in it. Before you treat an order as paid, read it from your server with GET /api/v1/merchant/orders/{orderId} and check that status is Confirmed and orderReferenceId is yours.

Build the return route

Use status only to choose which page to show while you check. Decide on the order you read back.

app.get('/qasimha/return', async (req, res) => {
  const orderId = String(req.query.orderId ?? '');
  const stored = await findOrderByQasimhaId(orderId);          // the id you saved at creation
  if (!stored) return res.redirect('/cart');

  const order = await qasimha.orders.get(orderId);
  if (order.status === 'Confirmed' && order.orderReferenceId === stored.reference) {
    await markPaid(stored.reference);                           // safe to repeat: the webhook may have done it
    return res.redirect(`/orders/${stored.reference}/thank-you`);
  }
  return res.redirect(`/checkout?payment=${encodeURIComponent(order.status ?? "")}`);
});

If you only kept your own reference, read the order with GET /api/v1/merchant/orders/by-reference/{orderReferenceId}. The customer may also never come back (for example, they close the browser), so always handle the webhook too.

Per environment

Build the URLs from each deployment's own base URL, so a UAT order returns to your test site and a production order to your live site. Never point production orders at a test host.

Mobile apps

Use one of these as the return URL, and register the same value in your app:

  • An https URL on your own domain that opens the app (Android App Links, iOS Universal Links). This is the secure choice.
  • A custom scheme such as myshop://qasimha/return. It is simpler, but another app can claim the same scheme.

The mobile SDKs open paymentUrl in the system browser, watch for navigation to any of your three URLs, and return a typed result: success (confirmed), failure (declined, expired, failed) or cancelled (cancelled, or the customer closing the sheet). Your app then asks your server to confirm the order.

Troubleshooting

400 VALIDATION_ERROR on MerchantUrl.Failure

You sent success without failure (or the reverse). Send both. They can be the same URL.

400 VALIDATION_ERROR on MerchantUrl.Notification

notification is reserved. Remove it and register a webhook endpoint instead.

My http:// URL is refused

Plain http is allowed only for localhost and 127.0.0.1. Use https everywhere else, including UAT test sites.

The customer returned with status=confirmed but the order isn't Confirmed

Trust the order you read, not the parameter. Show a "payment pending" page and wait for the webhook, or read the order again shortly.

Related





Did this page help you?