Google Pay payin

Charge the card behind a Google Pay token (ECv2). Some cards ask for 3DS: the payer passes it on the payment page.

Use the key of a payin shop; a payout shop key gets 403 method_not_enabled. Rules shared by every method — Authentication, Idempotency, Errors, Webhooks — apply here and are not repeated.

Flow

  1. Get paymentMethodData.tokenizationData.token from the Google Pay API. Generate merchant_reference and save it with your order.
  2. Create the payin: POST /v2/payins/google-pay with the token in Base64. Save id.
  3. If the webhook payin.requires_action comes, send the payer to next_action.redirect.url — the payment page. It runs 3DS, then sends the payer to return_urls.
  4. Wait for the webhook payin.completed or payin.failed. Verify the signature and act on data.object — the full payin.
  5. Fallback: if no webhook has come for a minute, you can start polling the status with GET /v2/transactions/{transaction_id} until it is final. Polling instead of webhooks loads the API and gets 429 rate_limited (R20).

Endpoints

Step Endpoint Operation
Create POST /v2/payins/google-pay createGooglePayPayin
Refund POST /v2/payins/{payin_id}/refunds — common for all payins, see Refunds createRefund
Read one GET /v2/transactions/{transaction_id} — common for all transactions, see Read one transaction retrieveTransaction

The create has its own section below. Reading returns the Payin object.

POST /v2/payins/google-pay

Creates a payin from the Google Pay token.

Request

Headers: Authorization: Bearer <payin shop key>, Content-Type: application/json.

Field Type Required Format and limits
merchant_reference string yes 1–100 letters, digits, _, -. Unique per merchant across payins, refunds, payouts and pay sessions. The idempotency key of this call
amount object yes
amount.value string yes Major units, at most two decimals, at least 0.01: "75.00". A string, never a number
amount.currency string yes ISO 4217, the shop currency: EUR
payer object yes The person who pays
payer.first_name string yes Letters, digits, spaces, ., ', -
payer.last_name string yes Letters, digits, spaces, ., ', -
payer.email string yes Email address
payer.ip_address string yes IPv4 or IPv6 of the payer
payer.address object yes Billing address
payer.address.country string yes ISO 3166-1 alpha-2: US
payer.address.state string for US, CA, AU payers ISO 3166-2 subdivision code without the country prefix, 2–3 letters: CA
payer.address.city string yes Not digits only
payer.address.postal_code string yes
payer.address.line1 string yes Street and house number
payer.address.line2 string no Apartment, suite, floor
google_pay object yes
google_pay.payment_token string yes Base64 of paymentMethodData.tokenizationData.token. Protocol ECv2
return_urls object yes
return_urls.success string yes https:// URL. The payer is sent there after completed
return_urls.failure string yes https:// URL. The payer is sent there after failed
curl -X POST https://payments-api.mrcr.io/v2/payins/google-pay \
  -H "Authorization: Bearer $PAYIN_SHOP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_reference": "order-30001",
    "amount": { "value": "75.00", "currency": "EUR" },
    "payer": {
      "first_name": "Jane", "last_name": "Doe", "email": "jane@example.com", "ip_address": "203.0.113.10",
      "address": { "country": "US", "state": "CA", "city": "San Francisco", "postal_code": "94105", "line1": "1 Market Street" }
    },
    "google_pay": { "payment_token": "eyJzaWduYXR1cmUiOiLigKYiLCJpbnRlcm1lZGlh…" },
    "return_urls": { "success": "https://shop.example.com/pay/success", "failure": "https://shop.example.com/pay/failure" }
  }'

Response 201 Created

The Payin object in data, in created. Any status is possible, including a final one when the call repeats an earlier one.

Header Meaning
Idempotent-Replayed: true The same merchant_reference and body were sent before. This is the existing payin in its current status; nothing was created again
{
  "data": {
    "id": "4f0d8c6e-1a5b-4c2f-9e7d-9b6a2c5f8eb4",
    "object": "payin",
    "merchant_reference": "order-30001",
    "method_type": "payin_google_pay",
    "status": "created",
    "status_details": null,
    "amount": { "value": "75.00", "currency": "EUR" },
    "refunded_amount": { "value": "0.00", "currency": "EUR" },
    "next_action": null,
    "created_at": "2026-09-29T09:20:00Z",
    "updated_at": "2026-09-29T09:20:00Z"
  }
}

Errors

No payin is created on any of them.

HTTP code When Retry
400 validation_failed A field is missing or malformed — details[] names it. Also: the amount has more than two decimals or is not in the shop currency No. Fix the request
401 authentication_failed The key is missing, malformed, revoked, or sent to the wrong host No
403 method_not_enabled Google Pay is not enabled for the shop, or the key is of a payout shop No. Use the payin shop key, or ask Mercuryo to enable the method
409 merchant_reference_conflict The reference was used with a different body No. Resend the original body, or use a new reference for a new payin
429 rate_limited Too many requests Yes, after Retry-After seconds
500 internal_error Failure on our side Yes, the identical request

After a timeout or a dropped connection the payin may or may not exist. Resend the identical request (R4), never with a new reference.

Payer step: 3DS

When the card asks for 3DS, the payin goes to requires_action and the webhook payin.requires_action comes with next_action.type: redirect:

  • Send the payer to next_action.redirect.url before next_action.expires_at. Open it as a full page or a new window, not in an iframe.
  • After the final status the payer is sent to return_urls.success or return_urls.failure. Coming back is not a result — act on the webhook.

How to open the page in a browser or an app — Payer actions.

The Payin object

Returned by the create and by GET /v2/transactions/{transaction_id}, and sent in webhooks as data.object. It has the common fields of every transaction, with these values and fields for a Google Pay payin:

Field Type Always set Meaning
id string yes UUID. Use it in refunds and GET /v2/transactions/{transaction_id}
object string yes payin
method_type string yes payin_google_pay
status string yes created, processing, requires_action, completed, failed, refunded — see Statuses
status_details.code string when failed See Decline reasons
refunded_amount object yes Sum of completed refunds; above zero in refunded
refunded_amount.value string yes Major units: "20.00"
refunded_amount.currency string yes ISO 4217: EUR
next_action object or null yes Set in requires_action, null in every other status
next_action.type string in requires_action redirect
next_action.redirect.url string in requires_action The payment page; it runs 3DS
next_action.expires_at date-time no When the provider stops waiting for the payer; after it the payin ends failed
google_pay object no Set after the token is decrypted
google_pay.brand string no Card scheme: visa, mastercard
google_pay.last4 string no Last four digits of the card number

Statuses

Status What it means The money Webhook What you do Next
created Accepted and recorded — — Wait processing, failed
processing The token is decrypted, checks run, the payin is at the provider — — Wait requires_action, completed, failed
requires_action The payer has to pass 3DS — payin.requires_action Send the payer to next_action.redirect.url processing, failed
completed Final. Charged Charged payin.completed Fulfil the order refunded
refunded Final. Charged, then refunded in part or in full. refunded_amount says how much Refunded in part or in full payin.refunded on every completed refund Reflect the refund in your order —
failed Final. Not charged. status_details.code says why — payin.failed Act on the decline reason —
  • Only completed, failed and refunded are outcomes. A final status never changes, with one exception: a completed refund moves the payin from completed to refunded.
  • An unknown status is not final — keep waiting (R13).

Decline reasons

status_details.code on a failed Google Pay payin. Every decline is final: a new attempt is a new payin with a new merchant_reference.

Code When What you do
invalid_payment_token The token could not be decrypted Ask the payer to pay again in Google Pay
card_not_supported The card's BIN is unknown or not accepted Ask the payer for another card
method_unavailable No route for this card right now Try again later with a new payin
risk_rejected Rejected by antifraud Do not retry automatically
provider_declined The provider or the issuer declined, including a failed 3DS Ask the payer to try again or use another card
internal_error Failure on our side; nothing was charged Try again with a new payin. Quote id to support if it repeats

An unknown code: handle it like provider_declined.

Webhooks

type When data.object
payin.requires_action The card asks for 3DS The Payin object, with next_action
payin.completed The payin reaches completed The Payin object
payin.refunded A refund of the payin completes — the first one moves it to refunded The Payin object, with refunded_amount
payin.failed The payin reaches failed The Payin object, with status_details
refund.completed, refund.failed A refund of this payin reaches a final status The Refund object — see Refunds

Headers, signature check and redelivery — Webhooks.

In the sandbox

google_pay.payment_token: test_google_pay_token_visa, or a real token from Google Pay in its TEST environment. The cents of amount.value select the outcome:

Cents Example Outcome
any other 75.00 completed
.51 75.51 failed — provider_declined
.52 75.52 failed — risk_rejected
.54 75.54 failed — card_not_supported
.55 75.55 failed — method_unavailable
.56 75.56 failed — internal_error
.57 75.57 requires_action, then completed after the payer step
.58 75.58 requires_action, then failed — provider_declined
.61 75.61 processing for 60 seconds, then completed

Sandbox keys and fixtures — Sandbox.

Next: BLIK