Skip to main content
Some wallets host third-party mini programs — small apps that run inside the wallet’s own app. If you publish one, you can collect payment inside it without sending the user out to a browser: your mini program asks the wallet to pay, and your backend talks to Waffo exactly as it does for any other order. This page covers the whole integration: the payment path, the refund path, and the separate user-identity path. Signing, keys and headers are unchanged from every other Waffo API — see Authentication & Signature. Order fields not specific to mini programs are in the Order Creation Field Guide.
Publishing a mini program requires a developer account on the wallet’s own mini-program platform, and Waffo assigns you the app identifier you send on API calls. Ask your Waffo integration contact to start that process before you write any code — the identifiers are not self-service.

What do I actually have to build?

Two independent tracks. Payment is mandatory; identity is mandatory only in its first step. Payment track Identity track Both identity endpoints sit in the payin scope of an API key, so the same key you use for order creation can call them.

How does the payment flow run end to end?

  1. The user taps Pay in your mini program. Your front end calls your own backend, not Waffo directly — your API key and your signing private key must never reach the front end.
  2. Your backend calls /api/v1/order/create with the mini-program fields below, and receives a payment URL.
  3. Your front end passes that URL to my.tradePay.
  4. The wallet runs its own payment UI and returns success or failure to your front end.
  5. Waffo sends the authoritative result to your notifyUrl. Your backend fulfils the order on that, not on step 4.
That last point is the one that causes real incidents. The value my.tradePay hands back is a UI outcome: it tells you what happened in the user’s app, not what happened to the money. A user who backgrounds the wallet, loses signal, or force-closes the app can leave you with no front-end result at all on a payment that succeeded. Treat the webhook — or an /api/v1/order/inquiry poll — as the only source of truth, exactly as described in Confirming Payment Results.

What do I send on order creation?

Everything a normal order needs, plus three mini-program specifics: userTerminal matters more than it looks: it is part of how Waffo decides whether a payment method can serve this particular request. A mini-program order submitted as WEB can be rejected as having no usable payment method, because not every method supports every terminal. The other accepted values are WEB, APP and IN_WALLET_APP (the user is inside the wallet app but not inside a mini program). If you are unsure which payment-method name and which currency your account is configured for, read it back from /api/v1/paymethodconfig/inquiry — see Payment Method Configuration & Order Limits.

How do I refund a mini-program payment?

Through the ordinary refund API — /api/v1/order/refund, with /api/v1/refund/inquiry or the refund webhook to follow the outcome. There is no mini-program-specific refund endpoint, and no mini-program-specific rule about how many refunds an order may take. What does vary is the payment method’s refund capability: whether partial refunds are allowed at all, and whether they can be repeated. Read refundAllowed from the payment-method configuration rather than assuming, and see Payment Method Refund Limits for what each capability means in practice.

How do I identify the user inside my mini program?

Your mini program cannot see who the user is by itself; the wallet mediates it.
  1. Front end calls my.getAuthCode with the scopes you need. It returns a short-lived authCode.
  2. Front end sends that authCode to your backend.
  3. Your backend calls /api/v1/userid/inquiry with merchantId, appId, the authCode, and appPlatform (the wallet-platform code Waffo assigned you).
  4. You get back:
Use userId as the key for your own account linking. Do not treat it as a phone number, an email address, or a cross-wallet identity — it is an opaque identifier scoped to your mini program.

Can I get the user’s email address so I can deliver the goods?

Not by default, and this is the single most common surprise in this integration. my.getAuthCode must include the scopes MINI_PROGRAM and PUBLIC_ID. Those two get you the public user identifier and nothing else. Everything else is an optional scope that has to be approved per merchant by the wallet platform: By default no personal information beyond the public ID is granted. To get any of the above, you have to request the specific scope and provide a written justification for why your mini program cannot function without it. Approval is the wallet platform’s decision, not Waffo’s, and it is granted scope by scope. Design accordingly: build the flow so that a user with only a public ID can still complete a purchase and receive what they bought. In practice that means collecting a delivery email in your own mini-program UI, and treating any profile field the wallet grants you as a convenience that pre-fills a form — never as a required input.

What does the profile lookup return?

POST /api/v1/userinfo/inquiry takes merchantId, appId, appPlatform and the accessToken from the identity step, and returns whatever your approved scopes cover: Every one of these is conditional. A field you were not granted simply does not arrive, and a field you were granted can still be absent because the user never filled it in. Write your mapping so that a missing field is a normal case, not an error.

What do the identity error codes mean?

The three B0008–B0010 codes are all “your auth code is no longer usable”. The correct handling for every one of them is the same: go back to the front end for a new code, rather than retrying the backend call. Payment errors are the ordinary ones — see Common Errors Troubleshooting.

Can I run subscriptions inside a mini program?

Yes, as a distinct product: MINI_PROGRAM_SUBSCRIPTION rather than MINI_PROGRAM_PAYMENT. It carries one extra requirement — a user access token obtained through the identity flow above must be supplied when the subscription is created, because the wallet needs to know which of its users is authorising the recurring charge. Everything else about subscriptions is unchanged, including amount rules, notifications and cancellation. Start from Subscription FAQ, and check what your payment method supports before promising features to your users.

What is different from a normal hosted-checkout integration?

The important consequence: none of the checkout-page settings apply. Display language, method ordering, branding and the result page all live in the wallet’s UI, so your mini program owns the experience. Everything in Checkout display rules and the checkout customisation pages is irrelevant to this path.

Do I still get the same reporting?

Yes. Mini-program orders appear in the Portal transaction list, in exports and in settlement like any other order, and are distinguishable by their product. Order identifiers behave identically, so your reconciliation does not need a special case — see Settlement Reports & Exports.

Is there a Waffo SDK or JS package for the mini program?

No. There is nothing for you to install on the mini-program side. The two halves of the integration are: Waffo’s published SDKs are server-side helpers for calling the REST API; they are not mini-program packages and there is no front-end bundle to embed. So “which Waffo library do I import into the mini program” has no answer — the correct answer is none, and your front end never talks to Waffo directly.

What is appPlatform for, and what happens if I leave it out?

appPlatform tells Waffo which wallet platform the auth code came from. It is part of the documented request contract for both identity endpoints, and the accepted values are assigned to you by Waffo along with your appId. Always send it. It is the field that disambiguates the exchange when your account is set up on more than one wallet platform, and omitting it is not something to rely on — there is no defined fallback behaviour for you to depend on, and the value costs you nothing to store next to the appId you already hold.

What does B0013 mean, and how is it different from B0011 and B0012?

For your integration all three collapse to the same recovery: go back to the front end, call my.getAuthCode for a new code, exchange it for a new token. Retrying the backend call with the same token cannot succeed for any of them. Do not build separate branches for the three. Build one “re-run the identity flow” path and route B0011, B0012 and B0013 into it, exactly as you do for B0008–B0010 on the auth code.

The token has not reached accessTokenExpiryTime, but creating a subscription says the token expired

Because the wallet, not Waffo, is the authority on whether the token is still good at the moment of the charge. accessTokenExpiryTime is the latest the token could be valid — it is not a guarantee it still is. A token can stop working early when the customer revokes the authorisation, signs out, or the wallet invalidates the session on its own. When that happens the wallet’s verdict reaches you as B0009 (auth code already expired) or B0008 (auth code invalid) on the create call, rather than from the identity endpoint. Both are classified as not retryable: the same call with the same credential will keep failing. The design rule that follows: fetch a fresh token as part of the flow that creates the order or the subscription, rather than reusing a stored one. Caching a token and treating accessTokenExpiryTime as a validity promise is the single most common cause of this error.

Can I run subscriptions in the mini program, and what exactly do I send?

Yes, as the MINI_PROGRAM_SUBSCRIPTION product. Alongside the normal subscription fields you send the customer’s access token: The token identifies which of the wallet’s users is authorising the recurring mandate, so the wallet can bind the mandate to the right account. The same field exists on /api/v1/subscription/change, so a plan change in a mini program needs a freshly obtained token too. Everything else about subscriptions is unchanged — cycles, trials, amounts, statuses, retries, notifications and cancellation all behave exactly as on Subscription creation & validation and the pages it links to.

Do the later renewals need a new token each time?

No. The token is supplied once, at signing time, to establish the mandate. The stored mandate is what later periods are charged against, so you do not re-run the identity flow on every renewal — renewals are background charges with no customer present and no front end involved. You need a fresh token when the customer is in front of you authorising something new: creating a subscription, or a plan change. Not for period 2 onwards. If a renewal fails because the customer revoked the mandate in their wallet, that arrives as a cancellation on the payment-method side rather than as a token error — see the wallet-revocation behaviour in Subscription FAQ.

Can I send the customer back to a specific page in my mini program after paying?

That is your front end’s job, not Waffo’s. Because there is no Waffo-hosted page anywhere in this flow, the redirect URLs on order creation have nothing to act on here — navigation stays inside the wallet, and control returns to the page that called my.tradePay when the wallet’s payment UI closes. So: resume on the page you left, and decide what to show from your backend’s view of the order, which is driven by the webhook. Do not render a success screen purely on the JS callback, for the reasons in Confirming Payment Results.

Where in the Portal can I see which mini program a payment came from?

On the individual payment’s detail page, which carries a mini-program block with the App ID and App Name for orders that have one. Open the payment from Transactions → Payments and the block is there alongside the rest of the order. What is not available: there is no mini-program filter or App ID column on the payments list, so you cannot slice the list by mini program. If you run more than one mini program and need per-app reporting, key it off your own order records — you know which appId you sent on each /api/v1/order/create — rather than expecting the Portal to group it for you. Mini-program products are named in transaction exports, so the traffic is identifiable as mini-program traffic; it is the which mini program that is detail-page only.

Is the customer the same person as on my web checkout?

Not automatically, and you should not try to make Waffo answer this. The userId you get back is the wallet’s public identifier scoped to your mini program — it is not an email address, not a phone number, and not a cross-channel identity. That means:
  • You cannot match a mini-program shopper to a web-checkout shopper by comparing identifiers. There is nothing shared to compare.
  • If you publish two mini programs, do not assume the identifier for the same human is the same in both. Design as though it is not.
  • The workable pattern is your own account system: the customer signs in to your account inside the mini program, you store the wallet userId against that account once, and from then on your account ID is the identity that spans channels.
Where you have been granted a contact scope, an email address can help you offer a match to the customer — but treat it as a hint to confirm with them, never as proof that two records are the same person.

Where do I look next?

Stuck on the app identifier or a scope approval? Contact support — both are provisioning steps, not code problems.