Drive the merchant's PayPal onboarding
Call POST /api/channel/onboarding/start with the channel key and the
merchant’s X-Merchant-Id. Store the returned action_url and open it for that
merchant. Re-show the stored URL when the merchant returns to the onboarding
step instead of starting a new successful attempt unnecessarily.
curl --request POST \ --url https://staging.api.maxanapay.com/api/channel/onboarding/start \ --header 'Authorization: Bearer sk_test_replace_me' \ --header 'X-Merchant-Id: b3f1c8de-7a20-4c55-9e64-1d8f2a6b40c7' \ --header 'Content-Type: application/json' \ --data @start-onboarding.request.json{ "reconnect": false}{ "action_url": "https://www.sandbox.paypal.com/bizsignup/partner/entry?referralToken=EXAMPLE_REFERRAL_TOKEN"}The whole response is action_url, and the whole request body is optional:
sending {} is the same as sending {"reconnect": false}. The referral token
inside the URL is minted for this attempt and this merchant; it is not a
credential you store beyond the attempt.
In sandbox, ask us for the seller account the merchant signs in with, and for the personal account the buyer will pay with later; see Sandbox access is issued by Maxana. Both are PayPal sandbox accounts, and they are not interchangeable.
What the merchant sees
Section titled “What the merchant sees”PayPal’s seller-onboarding consent screen identifies maxanapay as the
partner requesting access, and asks the merchant to connect an account so it
can accept payments on that partner’s platform. It does not show the channel as
the PayPal partner. Tell the merchant that name before opening action_url:
the point of the warning is that the screen matches what they were told, so a
name they were not expecting is worse than no warning at all.
This exposure is limited to seller onboarding. When a buyer later opens the PayPal payment window, PayPal uses the merchant’s own business name. The buyer checkout is therefore merchant-branded; the seller-consent screen is the one place where the merchant sees the partner.
That name is production’s. The name comes from the PayPal application the referral was minted by, and sandbox runs a different application from production, so the sandbox screen shows a different name — reading it there and repeating it to a production merchant is how the wrong name gets announced. Take the name above for what you tell a merchant in production, and expect the screen you see while integrating in sandbox not to match it.
When the merchant comes back
Section titled “When the merchant comes back”A merchant may finish at PayPal without returning to the channel’s browser URL, so the return is navigation only, not a completion signal. Where the merchant actually stands is the status read, below.
Use {"reconnect": true} when a connected merchant must revisit PayPal to
grant missing access or connect another PayPal account. A successful call made
again is a new onboarding attempt, so keep the action URL already returned for
the current attempt.
A 409 means either that onboarding is already complete or that the merchant’s
PayPal link state changed while the referral was being minted. A state-change
conflict returns no action_url; read the current onboarding status and retry
only after any reported account conflict or administrative hold is resolved.
Where the merchant stands
Section titled “Where the merchant stands”GET /api/channel/onboarding/status reports the merchant’s latest known state.
curl --request GET \ --url https://staging.api.maxanapay.com/api/channel/onboarding/status \ --header 'Authorization: Bearer sk_test_replace_me' \ --header 'X-Merchant-Id: b3f1c8de-7a20-4c55-9e64-1d8f2a6b40c7'{ "merchant_id": "b3f1c8de-7a20-4c55-9e64-1d8f2a6b40c7", "onboarding_status": "completed", "paypal_connection_status": "connected", "paypal_status_reason": null, "payments_enabled": true, "checkout_enabled": true, "paypal_missing_scopes": [], "paypal_scope_feature_status": { "checkout_platform_fee": true, "disputes": true, "transaction_search": true, "reporting_reconciliation": true, "merchant_info": true, "advanced_card_checkout": false }, "paypal_acdc_status": "unavailable", "paypal_apple_pay_status": "unavailable", "paypal_google_pay_status": "unavailable", "paypal_primary_currency": "USD"}The response above is a merchant that has finished and can charge. Read
paypal_connection_status first — connected is the one value that means a
buyer can be charged — and the rest of this page
for what each other field answers when the merchant cannot.
The operation declares 13 fields, and answers with 5 of them every time:
merchant_idonboarding_statuspaypal_connection_statuspayments_enabledcheckout_enabled
The other 8 are optional, and which of them you receive
depends on the deployment your credential was issued for. The three capability
fields under What PayPal is still deciding are
the ones to expect missing: a deployment that does not report them leaves the
keys out of the response rather than answering null, so a reader that indexes
into them straight from the body fails on a field that is absent rather than
empty. Read every field outside the list above through a default.
paypal_primary_currency is the one that reports the currency to open sessions
in: see which of them a merchant can be paid
in and open
each session in it.
The read is not a live call to PayPal on every request, and it also keeps
answering from the last known state when PayPal cannot be reached, so a change
made at PayPal moments ago may not be in the answer yet. Treat
merchant.onboarding_updated as the signal that something moved, and a single
unchanged read as no evidence that nothing did.
It is not a passive read either, and that cuts both ways. When its snapshot is
stale, the call refreshes it from PayPal and stores what comes back — so the
state you are shown can be the state your own call just produced. A merchant
reporting paypal_scope_refresh_required can report connected on the very
next call, because reading is what fetched the grants that were missing. The
same refresh is what raises merchant.onboarding_updated, so one of your polls
can be why an event arrives.
A channel that polls is therefore doing something, and a channel that avoids polling to be gentle is preventing something. Call it where a merchant is waiting — the screen that tells them PayPal has not finished is exactly where this belongs — and stay inside the operation’s budget of
60 per 1 minute. A merchant nobody polls is not stranded either:
state moves without you, which is what merchant.onboarding_updated is for.
| Field | Answers |
|---|---|
merchant_id |
Which merchant this is. |
onboarding_status |
How far the merchant got. |
paypal_connection_status |
Whether a PayPal account is attached, and whether it is usable. |
paypal_status_reason |
What is holding the merchant up, when something is. |
paypal_status_reason_fresh |
Whether that reason reflects PayPal’s current answer or a stale one. |
payments_enabled |
Whether everything needed to charge has come together. |
checkout_enabled |
Whether the merchant can charge a buyer right now. |
paypal_missing_scopes |
Which access the merchant did not grant. |
paypal_scope_feature_status |
Which payment features are available for this merchant. |
paypal_acdc_status |
What PayPal is still deciding about advanced card checkout. |
paypal_apple_pay_status |
What PayPal is still deciding about Apple Pay. |
paypal_google_pay_status |
What PayPal is still deciding about Google Pay. |
paypal_primary_currency |
The account’s primary settlement currency; open sessions in it. |
Can the merchant take money?
Section titled “Can the merchant take money?”paypal_connection_status is the field that answers this, and connected is
the only value that means yes. It is the one value that requires all three of
PayPal reporting the seller ready, every access the platform needs granted, and
no administrative hold — which is why it is the field to gate a buyer on.
checkout_enabled answers something narrower: whether Maxana will let a payment
be created for this merchant. One it reports as false is refused with 403
when a PayPal payment is created, so there is nothing to gain by trying. One it
reports as true has cleared that gate and nothing beyond it.
It answers for the PayPal rail, which is the rail this page is about. A
merchant Maxana routes to another payment provider is not gated by it, so do
not read a false here as that merchant being unable to take any payment at
all.
payments_enabled is not a PayPal-only verdict, so do not read a false
there as a problem at PayPal. It is false unless PayPal reports the seller
ready, the merchant granted every access the platform needs, and no
administrative hold is in place. paypal_status_reason is what says which of
those it was.
The merchant that passes every check and fails at capture
Section titled “The merchant that passes every check and fails at capture”The two booleans can disagree, and the case where they do is the expensive one. A seller whose PayPal account can receive money and holds a checkout product, but whose email address PayPal has not confirmed, answers like this:
| Field | Value |
|---|---|
paypal_connection_status |
linked_pending |
paypal_status_reason |
email_unverified |
payments_enabled |
false |
checkout_enabled |
true |
checkout_enabled is true there, and everything a channel can do works: the
session opens, the buyer signs in at PayPal, the buyer approves. The capture is
then refused, because PayPal will not be paid into an account whose email it has
not confirmed. That refusal arrives as a 400 on the capture call, naming the
merchant’s PayPal account as unverified — after the buyer has authorized the
payment and believes they have paid.
So checkout_enabled: true is not permission to charge, and a merchant can pass
every check on this page and still fail at capture. Gate the buyer on
paypal_connection_status: "connected", which this merchant is not. In sandbox,
which PayPal account the merchant
connected
decides whether it can ever reach that state.
The events say the same thing, which is what a channel reconciles from rather
than the buyer’s browser. The sequence you observe is payment.approved, then
payment.failed naming the refusal in reason. The payment reaches a terminal
state and stops there.
Not every failed attempt raises that event, so its absence is not evidence a
payment succeeded. A decline the buyer can act on — another funding source,
another approval — is reported on the buyer’s page and raises no
payment.failed, because the same checkout can still be paid; and an attempt
whose outcome could not be read raises nothing until it resolves. Reconcile
those rather than waiting on an event.
payment.failed ends the payment attempt, not the checkout. The same session
can carry another attempt, and that attempt is a new transaction with events of
its own, so do not read the event as the end of the order.
The reason stays readable after the delivery, on the transaction itself, as
failure_code and failure_message. A delivery you missed is not the only
place it was ever stated.
Nothing the buyer does clears a refusal that names the merchant’s PayPal
account, though: until paypal_status_reason changes, every attempt on that
merchant ends the same way.
What is holding the merchant up?
Section titled “What is holding the merchant up?”paypal_status_reason is null when nothing blocking has been recorded. On a
merchant that is not yet connected, null means nothing has been reported yet;
it is not a statement that the merchant is ready. Nine values are possible.
Read paypal_status_reason_fresh alongside it. This read refreshes the
merchant from PayPal before answering, but when a recent refresh is still
cached, or a provider error has forced a short back-off, the reason served is
the last one persisted rather than one derived just now. paypal_status_reason_fresh
tells the two apart: true means the reason — null included — reflects
PayPal’s current answer, so a null reads “connected, nothing to relay”;
false means the reason is not currently known, so a null must be read as
“unknown, poll again”, not as “fine”. Gate on paypal_connection_status in
either case; the marker only says how much to trust a null reason on its own.
paypal_status_reason |
What it means | What to do |
|---|---|---|
null |
Nothing blocking is recorded — but only trust this when paypal_status_reason_fresh is true. |
Read paypal_connection_status before offering checkout; null here is not the same as ready. If paypal_status_reason_fresh is false, treat it as unknown and poll again. |
email_unverified |
PayPal has not confirmed the email address on the seller’s account. | Ask the merchant to confirm that address from their PayPal account — in sandbox, see below first. |
payments_not_receivable |
PayPal will not let the account receive money yet. | The merchant resolves this inside their PayPal account. |
missing_checkout_capability |
PayPal has not granted the account a checkout product. | Read the status again later; nothing on the channel side changes it. |
missing_paypal_scopes |
The merchant linked an account but withheld access the platform needs. | Send the merchant back with {"reconnect": true} and have them accept every permission. paypal_missing_scopes names what is missing. |
paypal_scope_refresh_required |
An account is attached but the access it granted has not been read back yet. | Read the status again. If it persists, send the merchant back with {"reconnect": true}. |
paypal_account_already_linked |
The PayPal account the merchant chose is already attached to a different merchant. | Have the merchant onboard a different PayPal account, or contact Maxana to release the existing link. |
admin_payment_hold |
Maxana has stopped payments for this merchant. | Contact Maxana. Nothing the merchant does at PayPal clears it. |
revoked |
The merchant is no longer connected and PayPal reports no problem of its own: consent was withdrawn, or the disconnection was asked for on Maxana’s side. | Start onboarding again; a successful reconnection clears it. |
One PayPal account, one merchant
Section titled “One PayPal account, one merchant”A PayPal account can be attached to exactly one Maxana merchant, and that is true across the whole platform rather than within your channel. The merchant holding it may be another of yours or a merchant of a channel you cannot see; the answer is the same either way and nothing here tells you which.
Only the first remedy on that row is yours to act on: have the merchant onboard a different PayPal account. Releasing the existing link is a support request to Maxana, not an operation this API exposes, so treat it as something to start early rather than something to wait for.
Merchants choose their own PayPal accounts, so expect this the first time two of them choose the same one.
The sandbox seller that cannot confirm its email
Section titled “The sandbox seller that cannot confirm its email”email_unverified has a remedy in production and none in sandbox. A sandbox
seller created inside PayPal’s own sign-up, rather than in the PayPal Developer
Dashboard, ends onboarding with its address unconfirmed and offers the merchant
no way to confirm it: the account’s own email settings show it unconfirmed with
nothing to click. Nothing you or the merchant can do from the channel clears it,
and the capture stays refused.
Start again with a business account created in the PayPal Developer Dashboard, which arrives confirmed. In production the row’s remedy stands as written.
Did the merchant finish the wizard?
Section titled “Did the merchant finish the wizard?”Read the section above before this pair. Where a reason is recorded, that is the field saying what happened, and the pair below can read as though the merchant never came back from PayPal when they did.
onboarding_status alone does not separate a merchant that is still filling in
PayPal’s form from one that finished and is waiting on PayPal. Read it together
with paypal_connection_status, which is what says whether a PayPal account is
attached at all.
onboarding_status |
Meaning |
|---|---|
pending |
The merchant exists and nothing has been started. No one has been sent to PayPal. |
in_progress |
Onboarding was started. Whether the merchant reached the end of PayPal’s form is in paypal_connection_status. |
completed |
A PayPal account is attached and everything Maxana requires has been granted. |
revoked |
PayPal readiness for this merchant lapsed, or its access was withdrawn. paypal_status_reason says which, and only withdrawn access is fixed by starting onboarding again. |
paypal_connection_status |
Meaning |
|---|---|
not_linked |
No PayPal account is attached. With in_progress and no reason recorded, onboarding was started and no account has come back from PayPal. With a reason recorded, one came back and the link was refused. |
linked_pending |
The merchant finished at PayPal and an account is attached, but PayPal has not granted everything the merchant needs. Read paypal_status_reason. |
connected |
An account is attached and usable. |
restricted |
An account may be attached, but payments are stopped. Read paypal_status_reason. |
So in_progress with not_linked and no reason recorded is a merchant that has
not come back from PayPal, and in_progress with linked_pending is one that has and is now
waiting on PayPal. The two need different messages, which is why the pair is
worth reading rather than the status alone. Neither says whether the merchant
ever opened the link: in_progress is recorded when the channel starts
onboarding, so a merchant that received the URL and ignored it looks exactly
like one that opened it and gave up.
There is one case the pair does not just miss but inverts. A merchant whose
chosen PayPal account is already attached to another Maxana merchant reads
in_progress and not_linked — indistinguishable from a merchant who never
went — while PayPal has told that merchant they are connected and ready to
receive payments. Nothing was refused at PayPal; the link was refused here.
paypal_status_reason is paypal_account_already_linked, and it is the only
field that separates the two.
Which payment features has PayPal granted?
Section titled “Which payment features has PayPal granted?”paypal_missing_scopes lists the access the merchant did not grant, drawn from
PAYMENT, REFUND, and PARTNER_FEE. An empty list is not proof that
everything was granted: it is also what a merchant with no attached PayPal
account returns, because there is nothing yet to evaluate. Pair it with
checkout_enabled.
paypal_primary_currency is the merchant’s primary PayPal settlement currency —
USD, BRL, and so on — read from PayPal once the account is linked, and
null until then. Maxana opens a PayPal session only in this currency (its own
restriction, not a claim that PayPal could not process another), so a session in
a different currency is refused at creation; open each session in this currency
rather than asking the merchant which one their account takes. While it is still
null, that check cannot run, so treat an unknown settlement currency as not yet
proven for a second currency; see which of them a merchant can be paid
in.
paypal_scope_feature_status maps each payment feature to whether it is
available for this merchant. Six keys are always present.
| Key | Feature |
|---|---|
checkout_platform_fee |
Charging a buyer through PayPal with the platform fee applied. |
disputes |
Reading and responding to the merchant’s disputes. |
transaction_search |
Searching the merchant’s PayPal transactions. |
reporting_reconciliation |
Reconciling the merchant’s settlement against PayPal. |
merchant_info |
Reading the merchant’s PayPal account details. |
advanced_card_checkout |
Collecting card details in fields hosted on the merchant’s own page. |
A connected merchant commonly has advanced_card_checkout set to false while
every other key is true. That merchant can take PayPal payments today and
cannot present card fields; PayPal grants advanced card processing separately
and on its own schedule. Read this key before offering card fields rather than
letting the merchant find out at checkout, and paypal_acdc_status below for
what PayPal is doing about it.
These keys are necessary, not sufficient. Venmo and Pay Later additionally
require a merchant in the United States, and Pay Later is withdrawn again from a
merchant that has turned it off, so checkout_platform_fee on its own is not a
reason to advertise either one.
What PayPal is still deciding
Section titled “What PayPal is still deciding”PayPal underwrites some payment capabilities separately from the account itself — advanced card checkout, Apple Pay and Google Pay — and the last three fields report where each of them stands:
paypal_acdc_statuspaypal_apple_pay_statuspaypal_google_pay_status
Each is null until PayPal has answered for that merchant, and then carries
one of five values. The middle column lists the PayPal statuses each value is
reached from — a capability’s own status, or the vetting_status of the
product behind it. It is a vocabulary, not a rule: seeing one of those
statuses is not on its own enough to produce the value beside it.
| Value | PayPal reports | What it means, and what to do |
|---|---|---|
unavailable | SUBSCRIBED_WITH_LIMIT | Maxana has no usable answer for this capability. Usually PayPal was never asked for it, or it is not offered where the merchant trades. It also covers the statuses named alongside, where PayPal did answer but not in terms that reduce to one value. Do not rely on the method. If the merchant says it works but keeps hitting a ceiling, that is the restricted case, and the limit is theirs to take up with PayPal. |
in_review | IN_REVIEW, PENDING, APPROVED | PayPal has the capability in hand and has asked the merchant for nothing: a review is running, or it is approved but not usable yet. Wait. |
need_more_data | NEED_MORE_DATA, NEED_DATA | PayPal needs supplementary information from the merchant before it can finish. Tell them today; nothing moves until they supply it at PayPal. |
denied | DENIED, DENY, DECLINED, SUSPENDED, REVOKED, INACTIVE | PayPal will not let the merchant use it — refused, or withdrawn after being granted. Waiting resolves neither. PayPal asks a refused merchant to apply again; a withdrawn capability has to be taken up with PayPal. |
active | ACTIVE, SUBSCRIBED | PayPal granted it. Offer the method at checkout. |
active is the one value that needs both halves to agree: the product vetted
as SUBSCRIBED (or ACTIVE) and the capability itself ACTIVE. A
subscribed product whose capability PayPal has not turned on yet is not
active. The other four are reached from either half on its own.
unavailable also absorbs the states below, where PayPal answered but not in
terms that reduce to one value:
SUBSCRIBED_WITH_LIMIT— PayPal finished provisioning under a restriction, such as a transaction limit. The restriction itself lives in a limitations array Maxana reads nowhere, so calling this granted would advertise a capability whose ceiling the caller cannot see.
in_review and need_more_data both leave the capability unusable, and only
one of them is waiting on the merchant. Read them before answering a merchant
who asks why a payment method has not appeared.
One caveat on the two wallets. PayPal reports advanced card checkout as a
product, where it states separately that a review is in progress and that a
merchant owes it information. Apple Pay and Google Pay it reports only as
capabilities, and its guidance for those groups “reviewing” and “needs data”
together. So in_review on advanced card checkout means PayPal has asked the
merchant for nothing; on a wallet it is the weaker statement that PayPal has
not said what it is waiting for. If a wallet sits in in_review and does not
move, have the merchant check their PayPal account for an open request.
Current limits
Section titled “Current limits”The channel cannot replace the partner identity on PayPal’s seller-consent screen. There is no channel API operation that marks onboarding complete, disconnects a channel merchant, or chooses PayPal capabilities. PayPal controls eligibility, and the status contract reports the result.
The reason a merchant is not connected at all is reported at the account level,
where the same distinction is not yet drawn: a merchant PayPal is still
reviewing and a merchant PayPal has asked for documents both read as
payments_not_receivable or missing_checkout_capability, the same values
several other causes produce. The per-capability values above are the only place
the contract separates the two today. Where it does not, the notices on the
merchant’s own PayPal account are what says why.
Continue to Register group webhooks.