Process a credit card payment
This guide shows you how to process a payment with a credit card payments app extension.
What you'll learn
Anchor link to section titled "What you'll learn"In this guide, you'll learn how to do the following tasks:
- Understand how the payment flow is initiated
- Redirect the customer for 3-D Secure authentication, if applicable
- Confirm whether to proceed with a payment request, if applicable
- Resolve a payment
- Reject a payment
- Set a payment as pending
Initiate the flow
Anchor link to section titled "Initiate the flow"The payment flow begins with an HTTP POST request sent from Shopify to the payment session URL of your credit card payments app extension provided during app extension configuration. This request contains information about the customer and the order, and, for 3-D Secure, client details.
Shopify must receive an HTTP 2xx
response for the payment session creation to be successful.
If the request fails, then it's retried several times. If the request still fails, then the customer needs to retry their payment through Shopify checkout.
If there's an error on the payments app's side, then don't respond with an HTTP 2xx
. Use an appropriate error status code instead.
Request headers
Anchor link to section titled "Request headers"Header | Description |
---|---|
Shopify-Shop-Domain Required |
The permanent domain of the shop. Can be used to identify which shop is initiating the request. |
Shopify-Request-Id Required |
The unique request ID used to track specific requests for troubleshooting purposes. |
Shopify-Api-Version Required |
The API version selected in the payments app configuration. The version selected defines the response expected by the payments app. |
Request body
Anchor link to section titled "Request body"Attribute | Description | Type |
---|---|---|
id Required |
Unique identifier for the payment attempt. Used as the idempotency key. It can be assumed that requests with a given ID are identical to any previously received requests with the same ID. This ID must be surfaced to the merchant so that they can correlate Shopify orders with payments managed by the Partner app. | String |
gid Required |
Identifies the payment when communicating with Shopify (in GraphQL mutations, for example). | String |
group Required |
A customer might open multiple tabs in their browser for a given order. All of those tabs will be associated with the same group. As a result, Shopify can initiate multiple payment flows for the same id and group , redirecting to your app each time. Your app must only call the PaymentSessionResolve once per id and group . |
String |
amount Required |
The amount to be charged. The value is always sent using a decimal point as a separator, regardless of locale. | Numeric |
currency Required |
Three-letter ISO 4217 currency code. | String |
test Required |
Indicates whether the payment is in test or live mode. Refer to Test mode for more information. | Boolean |
merchant_locale Required |
IETF BCP 47 language tag representing the language used by the merchant. | String |
payment_method Required |
Hash giving details on the payment method used. Refer to payment_method hash for more information. | Hash |
proposed_at Required |
Can be used to order payment attempts that are a part of the same group. | String (ISO-8601) |
customer |
If customer is included, then at least one of customer.email or customer.phone_number is present. For more information on the customer field, refer to the customer hash. |
Hash |
kind Required |
Either sale or authorization . For sale transactions, you need to capture the funds instantly with this request. For authorization transactions, you must place a hold on the funds and capture them later when Shopify sends a capture request. |
String |
client_details |
Details about the browser of the customer, if your credit card payments app extension supports the 3-D Secure authentication customer feature. For more information on the client_details field, refer to the client_details hash. |
Hash |
payment_method
hash
Anchor link to section titled "payment_method hash"Attribute | Description | Type |
---|---|---|
type Required |
The type of payment method. Possible values: ["credit_card" ]. |
String |
data Required |
Contains data relevant for the chosen payment method. Refer to payment_method.data hash for more information. | Hash |
payment_method.data
hash
Anchor link to section titled "payment_method.data hash"Attribute | Description | Type |
---|---|---|
fingerprint Required |
The fingerprint of the certificate that’s used to encrypt the credit card information of the customer. | String |
encrypted_message Required |
The encrypted credit card information of the customer strictly encoded as Base64. Learn how to decrypt encrypted_message and the structure of decrypted data. | String |
ephemeral_public_key Required |
The ephemeral public key that should be used with the private key to compute the shared secret, and to decrypt encrypted_message . |
String |
tag Required |
HMAC digest of the encrypted_message strictly encoded as Base64. |
String |
Decrypt encrypted_message
Anchor link to section titled "Decrypt encrypted_message"The credit card information of the customer is encrypted using ECIES hybrid encryption scheme. You can follow the following steps to decrypt the credit card information:
- Decode the Base64 encoded
encrypted_message
andtag
. - Use
fingerprint
to identify the certificate that's used for encryption. - Compute shared secret using the private key of the certificate and
ephemeral_public_key
. - Compute the hmac of decoded
encrypted_message
and ensure it matches the decoded value oftag
. - Decrypt
encrypted_message
.
Returned hash in API version 2023-04
:
Attribute | Description | Type |
---|---|---|
type Required |
Type of card. | String |
full_name Required |
Full name of the card holder. | String |
pan Required |
Card number. | String |
month Required |
Month of expiry. | Numeric |
year Required |
Year of expiry. | Numeric |
verification_value Required |
Security code. | String |
Returned hash in API version 2023-07
or later:
Attribute | Description | Type |
---|---|---|
type Required |
Type of card. | String |
data Required |
Contains data relevant to the encrypted message. Refer to encrypted_message_data hash for more information. | Hash |
encrypted_message_data
hash
Anchor link to section titled "encrypted_message_data hash"Attribute | Description | Type |
---|---|---|
full_name Required |
Full name of the card holder. | String |
pan Required |
Card number. | String |
month Required |
Month of expiry. | Numeric |
year Required |
Year of expiry. | Numeric |
verification_value Required |
Security code. | String |
customer
hash
Anchor link to section titled "customer hash"Attribute | Description | Type |
---|---|---|
email |
The email of the customer. Required if phone_number isn't present. |
String |
phone_number |
The phone number of the customer. Required if email isn't present. |
String |
locale Required |
The language code as an ISO 639-1 string and the country code as an ISO 3166-1 Alpha-2 string, if available. For more information on the locale field, refer to the locale fields changelog. | String |
billing_address Required |
The billing address of the customer. Refer to shipping/billing_address hash for more information. | Hash |
shipping_address Required |
The shipping address for the order. Refer to shipping/billing_address hash for more information. | Hash |
shipping/billing_address
hash
Anchor link to section titled "shipping/billing_address hash"Attribute | Description | Type |
---|---|---|
given_name |
The given name or first name of the customer. | String |
family_name Required |
The surname of the customer. | String |
line1 Required |
First line of the customer's address (for example, the street or PO box). | String |
line2 |
Second line of the customer's address (for example, the apartment or suite). | String |
city Required |
Name of city or town. | String |
postal_code |
Zip or postal code. | String |
province |
Name of the province or state. | String |
country Required |
The country code as an ISO 3166-1 Alpha-2 string. | String |
phone_number |
The phone number of the customer. | String |
company |
Name of company. | String |
client_details
hash
Anchor link to section titled "client_details hash"Attribute | Description | Type |
---|---|---|
ip_address Required |
The IP address from the browser of the customer. | String |
user_agent Required |
The HTTP user-agent header from the browser of the customer. | String |
accept_language Required |
The HTTP accept-language header from the browser of the customer. | String |
Redirect the customer (3-D Secure)
Anchor link to section titled "Redirect the customer (3-D Secure)"After the payments app has determined it needs to orchestrate a 3-D Secure authentication, and after the app has returned a response to HTTP POST request sent from Shopify to initiate the payment flow, the app uses the paymentSessionRedirect
mutation. Shopify renders the URL provided by the app in an iFrame
in the customer's browser.
The paymentSessionRedirect
mutation request must be received by Shopify within a given timeout, as described in the Timeout section. After the timeout, Shopify redirects the customer back to Shopify's checkout and will fail paymentSessionRedirect
mutations with the BUYER_ALREADY_REDIRECTED_BY_SHOPIFY
error code.
The id
argument corresponds to the global identifier (gid
) of the payment:
Confirm a payment (3-D Secure)
Anchor link to section titled "Confirm a payment (3-D Secure)"You must use the paymentSessionConfirm
mutation to confirm with Shopify whether to proceed with the payment request, according to Shopify's business logic (for example, oversell protection and discount code validation).
The id
argument corresponds to the global identifier (gid
) of the payment:
The payments app must redirect the customer when Shopify returns a PaymentSessionActionsRedirect.redirectUrl
.
When Shopify determines if the payment request can proceed, Shopify sends a POST request to the confirm session URL of the credit card payments app extension, delivering the confirmation result:
Shopify must receive an HTTP 2xx
response for the payment session confirmation to be successful.
If the request fails, then it's retried several times. If the request still fails, then the customer needs to retry their payment through Shopify checkout.
If there's an error on the payments app's side, then don't respond with an HTTP 2xx
. Use an appropriate error status code instead.
When Shopify indicates that the payment request cannot proceed, the payments app must invoke the paymentSessionReject
mutation using the CONFIRMATION_REJECTED
reason code.
Request headers
Anchor link to section titled "Request headers"Header | Description |
---|---|
Shopify-Shop-Domain Required |
The permanent domain of the shop. Can be used to identify which shop is initiating the request. |
Shopify-Request-Id Required |
The unique request ID used to track specific requests for troubleshooting purposes. |
Shopify-Api-Version Required |
The API version selected in the payments app configuration. The version selected defines the response expected by the payments app. |
Request body
Anchor link to section titled "Request body"Attribute | Description | Type |
---|---|---|
id Required |
The unique identifier for the payment, as provided in the HTTP POST request sent from Shopify to the payment session URL. Used as the idempotency key. It can be assumed that requests with a given id are identical to any previously received requests with the same id . |
|
gid Required |
Identifies the payment when communicating with Shopify (in GraphQL mutations, for example). | String |
confirmation_result Required |
Result of the payment confirmation initiated by the paymentSessionConfirm mutation. The payment app can proceed with the payment request if and only if confirmation_result is true |
Boolean |
Test the Confirm
mutation and the confirmation callback
Anchor link to section titled "Test the Confirm mutation and the confirmation callback"You can cause Shopify to send a POST
request to the confirm session URL of the credit card payments app extension with confirmation_result=false
by simulating that a product gets out of stock while the consumer is authenticating:
- Navigate to [Products > Inventory](https://help.shopify.com/en/manual/products/inventory/managing-inventory-quantities/track_inventory) in the Shopify admin.
- Make sure there is at least one unit in the inventory for a given product.
- Proceed to the payment with this product.
- Set the available inventory to 0 once the 3-D Secure challenge is presented.
- Complete the challenge.
- After your payments app calls the
paymentSessionConfirm
mutation, Shopify sends aPOST
request to the confirm session URL of the credit card payments app extension withconfirmation_result=false
.
Resolve a payment
Anchor link to section titled "Resolve a payment"You must use the paymentSessionResolve
mutation after the customer has successfully gone through the payment process. The id
argument corresponds to the global identifier (gid
) of the payment:
The authorizationExpiresAt
argument must be provided if the payment session request kind
is authorization
. The PaymentSessionThreeDSecureAuthentication
argument must be provided if the paymentSessionRedirect
mutation has been called for 3-D Secure.
Reject a payment
Anchor link to section titled "Reject a payment"The payments app should reject a payment if the customer can't complete a payment with the provider. The rejected payment tells Shopify that the checkout process will be halted. For example, if you don't want to process a high-risk payment, then you can reject the payment using the paymentSessionReject
mutation.
Rejecting a payment is final. You can't call other actions on a payment after it has been rejected.
To reject a credit card payment, you can use any of the following reason codes:
INCORRECT_NUMBER
INCORRECT_CVC
INCORRECT_ZIP
INCORRECT_ADDRESS
INCORRECT_PIN
INVALID_NUMBER
INVALID_CVC
INVALID_EXPIRY_DATE
EXPIRED_CARD
CARD_DECLINED
CONFIRMATION_REJECTED
PROCESSING_ERROR
You can reject a payment using the paymentSessionReject
mutation, as shown in the following example:
The rejection requires information on why the payment was rejected. This information is encapsulated in the PaymentSessionRejectionReasonInput
.
The PaymentSessionRejectionReasonInput.code
is a PaymentSessionStateRejectedReason
enum of standardized error codes.
The PaymentSessionRejectionReasonInput.merchantMessage
argument is a localized error message presented to the merchant explaining why the payment was rejected.
The PaymentSessionThreeDSecureAuthentication
argument must be provided if the paymentSessionRedirect
mutation has been called for 3-D Secure.
Set a payment as pending
Anchor link to section titled "Set a payment as pending"You can set a payment as pending if it's awaiting asynchronous action by the customer, the merchant, the payment Partner, or a payment network.
Not all payments can be processed and finalized quickly. Some payments can take several days to complete. Setting a payment as pending indicates to the customer and the merchant that you have started processing the payment, but require more time to complete the payment. Both sale
and authorization
payments can be marked as pending.
The process to set a payment as pending follows similar steps to a regular credit card payment, except instead of resolving or rejecting a payment, you first call the paymentSessionPending
mutation to set the payment as pending, before calling paymentSessionResolve
or paymentSessionReject
after the payment has been completed.
The following diagram illustrates how a payment flow works between Shopify and a payments app with a credit card payments app extension when setting a payment as pending:
- The customer completes checkout, triggering a request for payment.
- Shopify initiates the flow by sending a request to the payments app, specifying the amount and currency to be charged and the encrypted credit card information.
- The app responds with an HTTP code
200
(OK), indicating that the request is valid and has been received. - The payments app processes the payment with the payment information received in the request.
- The payments app calls the
paymentSessionPending
mutation to set the payment as pending. - Shopify responds with an HTTP code
200
(OK) to confirm the payment state is updated. - The customer continues to checkout.
- Upon completion, pending payments must be finalized into either a successful or failed state using the
paymentSessionResolve
orpaymentSessionReject
mutations.
To set a payment as pending, use the paymentSessionPending
mutation:
Credit card payment apps are expected to process payments in a few seconds. To ensure a good customer experience, the customer will be redirected to the order confirmation page when a payment times out. However the order status will be automatically set to Pending in the Shopify admin. You should still finalize the payment by calling the paymentSessionResolve
or paymentSessionReject
mutations.
If you don't meet the requirements, then Shopify can remove your app from the public list of payment gateways, suspend access to the payments ecosystem, terminate participation in the payments ecosystem, or take any other action deemed necessary.