| "Open this app from Shainee" |
You opened your page directly |
Expected. Launch it from Shainee, or sign in with the Playground while building |
| "Sign-in was rejected (invalid_client)" |
Wrong client id or secret, or sandbox credentials against the hosted service (or the other way round) |
Check IDP_URL, CLIENT_ID and CLIENT_SECRET match. In the sandbox they are sandbox_app and sandbox_secret |
| "Sign-in was rejected (unknown_or_expired_nonce)" |
The server restarted, or more than 2 minutes passed between steps 1 and 5 |
Reload the page |
| "Sign-in was rejected (nonce_mismatch)" |
Your page sent a different nonce than it announced |
Send the same nonce in ready and in /api/session |
"Sign-in was rejected (replayed)" or expired |
The token was used twice, or sat for more than 60 seconds |
Use the token once, straight away |
| "Launch refused: consent_required" |
The user revoked your app |
Reopen from the launcher; the consent sheet appears again |
| "Launch refused: unknown_app" |
The app is not Active, or the client id is wrong |
Ask the Shainee team to check the status |
| "Shainee did not answer" |
The page's address is not your registered origin (for example a redirect to another host or port) |
Keep every page on the registered origin |
| Blank in the phone app, fine in a browser |
Plain http, which iOS and Android release builds block |
Use HTTPS |
Verify fails with a 403 and an HTML page, not JSON |
The call has no custom User-Agent, and the firewall in front of the hosted service refused it |
Send User-Agent: my-app/1.0 on the verify call |
Creating a payment answers unknown_vendor |
A vendor in splits was never registered, or was registered by another app |
Register it with POST /api/partner/vendors first |
Creating a payment answers invalid_splits |
The split amounts do not add up to amount, or a vendor appears twice |
Work out the splits on your server from your own prices |
Your page never gets payment_result |
The page sent pay from an address that is not your registered origin, or the id is not one your server created for this user |
Send pay from your registered origin, with an id from POST /api/partner/payments for the signed-in sub |
Release answers nothing_held |
That split was already released or refunded |
Nothing to do: each split is paid out once. Read escrow with GET /api/partner/payments/{id} |
Release answers no_vendor_wallet |
Test mode only: the vendor's wallet number has no CityWallet account, so Shainee accepted the vendor but keeps its money held |
Register the vendor again with a real CityWallet number, then release |
Release answers provider_unreachable |
CityWallet did not answer |
The money is still held. Try again later |
Refund answers refund_unavailable |
Refunds are not available yet: CityPay Checkout does not say which wallet paid |
Keep the money held and contact the Shainee team, who settle it with the customer |
The payment stays processing |
CityPay has not confirmed it yet |
Wait. Shainee keeps asking CityPay; a payment.paid or payment.failed webhook (or GET /api/partner/payments/{id}) tells you how it ends |
| Webhooks are refused by your server's signature check |
The HMAC was computed over parsed and re-serialised JSON, or with your client secret |
Use the webhook secret, over <t>.<raw body> exactly as received |
| The Playground says "no answer" |
Your server is not running, is not on a public or reachable address, or does not allow this page's origin (CORS) |
See Making your server reachable |