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?
- 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.
- Your backend calls
/api/v1/order/createwith the mini-program fields below, and receives a payment URL. - Your front end passes that URL to
my.tradePay. - The wallet runs its own payment UI and returns success or failure to your front end.
- Waffo sends the authoritative result to your
notifyUrl. Your backend fulfils the order on that, not on step 4.
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.- Front end calls
my.getAuthCodewith the scopes you need. It returns a short-livedauthCode. - Front end sends that
authCodeto your backend. - Your backend calls
/api/v1/userid/inquirywithmerchantId,appId, theauthCode, andappPlatform(the wallet-platform code Waffo assigned you). - 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 theMINI_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 calledmy.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 whichappId 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. TheuserId
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
userIdagainst that account once, and from then on your account ID is the identity that spans channels.
Where do I look next?
- Confirming Payment Results — why the webhook, not the JS callback, is your source of truth.
- Order Creation Field Guide — the rest of the order-create contract.
- Payment Method Configuration & Order Limits — reading back which methods, currencies and amount ranges you have.