Errors
Last updated: 2026-09-26
Merchant API application errors use a google.rpc.Status-compatible JSON body:
{
"code": 3,
"message": "currencyCode is required",
"details": []
}
The JSON code is an API error code, not the HTTP status or a payment's
failureReasonCode. Use paymentOrder.status to determine a payment outcome.
If a non-success response is empty or cannot be read as JSON, do not infer a
payment result from it; follow the transport-error reconciliation guidance below.
Common code values:
| code | Meaning |
|---|---|
| 3 | INVALID_ARGUMENT (validation) |
| 5 | NOT_FOUND |
| 7 | PERMISSION_DENIED |
| 9 | FAILED_PRECONDITION (the request requires a condition to be resolved first) |
| 13 | INTERNAL |
| 14 | UNAVAILABLE (retryable only when the response provides retry guidance) |
| 16 | UNAUTHENTICATED |
Choose the next action
- For a field validation error, correct the named field before retrying. A pay-in reference conflict needs reconciliation of the existing payment instead of a new reference.
- For the account-level payout refusal, contact support. Repeating the same request or changing methods does not resolve the condition.
- For the documented retryable payout preparation response, honor the delay and retry the same request/reference a limited number of times.
- For a timeout, connection loss, or an error without a confirmed outcome, look up the existing payment before creating another attempt. HTTP status alone does not establish the financial outcome.
Validation error example (field violations)
{
"code": 3,
"message": "invalid argument",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "currency_code|payin.currency.required",
"description": "currencyCode is required"
},
{
"field": "amount|payin.amount.required",
"description": "amount must be greater than 0"
}
]
}
]
}
Use fieldViolations to map API validation errors to specific form fields on your side (for example highlight currencyCode input when currency_code|payin.currency.required is returned).
Nested field violations can use snake_case paths. Localized merchant validations may append a |<messageId> suffix, while other validation errors may use just the field path. For example, a missing payout operator can be reported as mobile_money_details.operator, which maps to the JSON field mobileMoneyDetails.operator.
Pay-in reference conflict
When a pay-in reference is already reserved and the request cannot be repeated,
the response is HTTP 400 / INVALID_ARGUMENT. For example:
{
"code": 3,
"message": "validation error",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{ "field": "PayIn/#merchant_reference", "description": "Taken" }
]
}
]
}
Read the payment by merchantReference and continue reconciling that attempt.
Do not change the reference to work around this response. The
pay-in retry rules distinguish an
equivalent repeat, a conflicting request and permitted reuse after cancellation
or expiry. If a repeat returns the same error, including HTTP 500, stop the
automatic retry loop and check the payment before contacting support.
Method temporarily unavailable
When a configured payment method or submethod rejects a request before
processing, the API can return HTTP 400 / INVALID_ARGUMENT with the public reason
method_unavailable and this message:
Payment method is temporarily unavailable. Please contact support.
The reason can appear in google.rpc.ErrorInfo.reason and as the suffix of a
field violation. The complete field value includes the operation prefix, for
example PayOut/#payment_method|method_unavailable. The prefix depends on the
operation; identify the condition from ErrorInfo.reason or the
|method_unavailable suffix, rather than comparing the entire field string.
A payment
accepted far enough to produce a payment order can instead expose
method_unavailable in paymentOrder.failureReason. Handle this reason
separately from malformed input, offer another discovered method when
appropriate, and do not retry in a tight loop.
Payout unavailable for the account
Payout creation can return HTTP 400 / FAILED_PRECONDITION (code: 9) with
the message below. Identify this condition by the exact public
google.rpc.PreconditionFailure violation type and subject:
{
"code": 9,
"message": "Payouts are currently unavailable. Please contact support.",
"details": [
{
"@type": "type.googleapis.com/google.rpc.PreconditionFailure",
"violations": [
{
"type": "merchant_payout_accounting_not_ready",
"subject": "merchant_payout",
"description": "Payouts are currently unavailable. Please contact support."
}
]
}
]
}
Contact support before retrying. Changing the payment method does not resolve this account-level condition. It describes the current request and does not establish the outcome of an earlier attempt; continue reconciling earlier payouts by their payment ID or reference.
Payout preparation temporarily unavailable
Payout creation can return HTTP 503 / UNAVAILABLE (code: 14) with retry
details when the service cannot prepare the requested payout. For the
operation_busy response described here, the rejected preparation has not
created a new payout, reserved funds, or submitted a payout for processing.
Read google.rpc.RetryInfo.retryDelay in details. The accompanying
google.rpc.ErrorInfo has reason operation_busy and string-valued retry
metadata. The relevant response fields are shown below; additional metadata
can be present:
{
"code": 14,
"message": "payment operation is temporarily busy",
"details": [
{
"@type": "type.googleapis.com/google.rpc.RetryInfo",
"retryDelay": "1s"
},
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "operation_busy",
"metadata": { "retryable": "true", "retryAfterSeconds": "1" }
}
]
}
Wait at least the returned delay, then retry an equivalent request with the
same merchantReference. Use increasing delays and a bounded retry policy;
if the response persists, stop automatic retries and contact support. An HTTP
Retry-After header is not required for this response; use the JSON details.
A missing or unsupported operator follows
its validation guidance instead.
Do not apply this no-new-payout guarantee to a generic HTTP 503, connection failure, or timeout. If the outcome is unknown, reconcile the existing attempt before attempting another payout.
Payment failure reasons
When a payment reaches a negative state, paymentOrder.failureReason and the
string-encoded paymentOrder.failureReasonCode can contain these current
values:
failureReason | failureReasonCode | Meaning |
|---|---|---|
internal_error | 1 | Processing error |
unknown | 2 | Unknown reason |
declined | 3 | Declined by the customer or payment partner |
cancelled_by_user | 4 | Customer cancelled the payment |
invalid_request | 5 | Invalid or unsupported payment request |
insufficient_funds | 6 | Insufficient customer or merchant funds, depending on the flow |
expired_payment | 7 | Payment session expired |
expired_payment_form | 8 | Hosted payment form expired |
suspected_fraud | 9 | Payment was rejected as suspected fraud |
provider_unavailable | 10 | Temporary processing unavailability after the payment was created |
amount_out_of_range | 11 | Amount is outside the supported range |
method_unavailable | 12 | Selected method or submethod is temporarily unavailable |
This is not an exhaustive list. Treat unrecognized reason strings or codes as unknown reasons, and use the payment status to determine the result. If an update follows an earlier completion, reconcile it with that same payment; see Later amount corrections.
Outcome vs transport status
- Validation/auth/system issues return non-2xx with
rpc.Status. - Payment outcome is carried by
paymentOrder.status(often in HTTP 200 responses). - For mobile-money pay-ins, missing
mobileMoneyDetailsormobileMoneyDetails.mobileNumberis a validation error and returns non-2xx. - For operator-aware mobile-money pay-ins and payouts, a missing required
mobileMoneyDetails.operatoris a validation error and returns non-2xx. - For mobile money transfer, missing
mobileMoneyTransferDetails,.operator, or.mobileNumberis a validation error and returns non-2xx. - For voucher-based mobile-money pay-ins, send
mobileMoneyDetails.voucherPinwhen the selected route requires voucher collection.