Skip to content

Callback URL

Receive payment results via browser redirect after payment completion.

Overview

When a user completes (or fails/closes) a payment, the SoloPay widget redirects the user to the successUrl or failUrl specified at payment creation. paymentId, orderId, and status are automatically appended as query parameters.

User completes payment

SoloPay widget redirects to successUrl or failUrl

Merchant page receives query parameters

Server calls GET /merchant/payments/:id to verify (required)

How It Works

  • Desktop: Payment proceeds in a popup window. After completion, the popup closes and the user is redirected to successUrl or failUrl.
  • Mobile: The user is redirected to a full-screen page for payment. After completion, the user is redirected to successUrl or failUrl.

Setting successUrl / failUrl

Specify successUrl and failUrl when calling the widget.

typescript
solopay.requestPayment({
  orderId: 'order-001',
  amount: '10.5',
  tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
  successUrl: 'https://yourshop.com/payment/success',
  failUrl: 'https://yourshop.com/payment/fail',
});
  • successUrl — Redirect on payment success
  • failUrl — Redirect on payment failure or when the user closes the widget

Query Parameters

The widget automatically appends the following parameters on redirect.

ParameterDescription
paymentIdThe unique payment identifier
orderIdThe merchant order ID
statusPayment result: success, fail, or closed
sigHMAC callback signature (t=timestamp,v1=signature). Present when merchant has a webhook secret configured. Not included for closed status.

Status Values

ValueRedirect URLDescription
successsuccessUrlPayment completed successfully
failfailUrlPayment failed due to transaction failure
closedfailUrlUser closed the widget before completing payment (payment may not have been created)

Example URLs

https://yourshop.com/payment/success?paymentId=0xabc123...&orderId=order-001&status=success&sig=t%3D1700000000%2Cv1%3Dabcdef...
https://yourshop.com/payment/fail?paymentId=0xabc123...&orderId=order-001&status=fail&sig=t%3D1700000000%2Cv1%3Dabcdef...
https://yourshop.com/payment/fail?paymentId=0xabc123...&orderId=order-001&status=closed

Frontend Handling Example

Parse the query parameters on your successUrl or failUrl page and verify the payment status via your server API.

typescript
// successUrl page (e.g., /payment/success)
const params = new URLSearchParams(window.location.search);
const paymentId = params.get('paymentId');
const orderId = params.get('orderId');
const status = params.get('status');

if (paymentId) {
  // Verify payment status on server (required)
  const response = await fetch(`/api/verify-payment?paymentId=${paymentId}`);
  const result = await response.json();

  if (result.verified) {
    // Show order completion screen
  } else {
    // Handle verification failure
  }
}
typescript
// failUrl page (e.g., /payment/fail)
const params = new URLSearchParams(window.location.search);
const status = params.get('status');

if (status === 'closed') {
  // Show "Payment was cancelled" message
} else {
  // Show "Payment failed. Please try again" message
}

Payment Verification (Required)

Do Not Trust URL Parameters

Query parameters can be manipulated by the user. Even if status=success, you must call the API from your server to verify the actual payment status.

Once you receive a paymentId, call GET /merchant/payments/:id from your server to verify the final status.

bash
curl https://gateway.dev.solonetwork.io/merchant/payments/0xabc123... \
  -H "x-api-key: sk_xxxxx"

Verification Checklist

  • [ ] Confirm status === 'PAID' (payment success)
  • [ ] Confirm amount matches the expected amount in your order database (the widget runs client-side and the amount could be tampered with)
  • [ ] Confirm tokenAddress matches the expected token contract address
  • [ ] Confirm orderId matches the expected orderId
  • [ ] Prevent duplicate completion processing for the same paymentId

HMAC Callback Signature Verification

When a webhook signing secret is configured for your merchant, the widget appends a sig query parameter to successUrl and failUrl redirects (except for closed status). This allows you to verify on your server that the redirect originated from SoloPay.

The signature format is the same as webhook signatures: t=timestamp,v1=signature, where the signed payload is timestamp.paymentId.orderId.status.

Using the SDK

typescript
import { verifyCallbackSignature } from '@solo-pay/gateway-sdk';

// On your success/fail page (server-side)
const { paymentId, orderId, status, sig } = queryParams;

if (sig) {
  const isValid = verifyCallbackSignature(
    sig,
    paymentId,
    orderId,
    status,
    process.env.WEBHOOK_SECRET
  );
  if (!isValid) {
    // Redirect may have been tampered with
  }
}

Always Verify via API

Even with a valid callback signature, you must still call GET /merchant/payments/:id from your server to verify the final payment status. The callback signature provides an additional layer of authenticity but does not replace server-side verification.

Limitations and Considerations

  • Callback URLs are browser-redirect based and can be lost due to network issues.
  • If the user closes the browser mid-payment or loses network connectivity, the redirect to successUrl/failUrl may not occur.
  • Callback URLs alone cannot guarantee payment result delivery.

Use Webhooks Together

To compensate for callback limitations, it is strongly recommended to use Webhooks alongside Callback URLs.

MethodDelivery PathReliabilityPurpose
Callback URLBrowserLowDisplay result page to user
WebhookServerHighUpdate order status on server
  • Callback URL — For user experience (showing result pages)
  • Webhook — For server-side order completion processing
  • Both channels require final verification via GET /merchant/payments/:id

Next Steps

Non-custodial Web3 payment infrastructure for ERC-20 checkout, sponsored gas, and wallet-to-wallet settlement.