Troubleshooting Payment Link & QR Code Errors
When a customer or POS cashier visits an Amwal payment URL (https://pay.sa.amwal.tech/payment-link/{id}), errors can arise if the link has expired, already been paid, or configured with invalid parameters.
Common Link Errors & Solutions
1. "Payment Link Has Expired" (PAYMENT_LINK_EXPIRED)
- Root Cause: The ISO 8601 timestamp in
selectedDatehas elapsed. - Default TTL: If not explicitly specified during creation, payment links expire after 24 hours.
- Solution:
- To generate persistent links for long invoices, set
selectedDateto a future timestamp (e.g.2026-12-31T23:59:59Z). - In POS in-store mode, QR codes are generated dynamically with shorter 15-minute expiry for security.
- To generate persistent links for long invoices, set
2. "This Link Has Already Been Used" (PAYMENT_LINK_ALREADY_PAID)
- Root Cause: The link was generated with
"singleUse": trueand a successful payment was already completed. - Solution:
- For single invoices, generate a new link for repeat customer orders using the Create Payment Link API.
- For reusable static donation or multi-buyer product links, set
"singleUse": false.
3. "Store Not Found or Inactive" (STORE_INACTIVE_OR_INVALID)
- Root Cause: The
store_idUUID in the path/payment_links/{store_id}/createdoes not exist or has been paused in the Merchant Portal. - Solution:
- Verify your Store ID under Merchant Portal -> Stores & Terminals.
- Ensure the store's verification status is active with an approved Commercial Registration (CR).
Programmatic Link Inspection
You can inspect the live status of any payment link using the Get Payment Link Details API — note this is a POST, and payment_link_id is the only path parameter (no separate store_id segment):
curl -X POST "https://backend.sa.amwal.tech/payment_links/d0901e69-02ec-499f-ba5c-9c7ef95ad062/details" \
-H "Authorization: YOUR_SECRET_KEY" \
-H "X-Amwal-Key: sandbox-amwal-xxx" \
-H "Content-Type: application/json"payment_link.status reverts to Unpaid after ANY refund
Confirmed against the live API: once a refund lands on a transaction —
even a partial one — the payment link's status reverts to Unpaid,
identical to a link nobody ever paid. Don't use payment_link.status
alone to diagnose "was this ever paid" — check
transactions[].status instead. See
Get Payment Link Details for
the full caveat.
