--- title: Shop Pay Wallet API reference description: Learn about the available Shop Pay Wallet API endpoints. api_name: shop-pay-wallet source_url: html: https://shopify.dev/docs/api/shop-pay-wallet/reference/index md: https://shopify.dev/docs/api/shop-pay-wallet/reference/index.md --- ExpandOn this page * [Authorizations](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#authorizations) * [Managing Tokens](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#managing-tokens) * [Wallet and Orders](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#wallet-and-orders) * [Order resources](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#order-resources) * [Completing a checkout](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#completing-a-checkout) * [Next steps](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#next-steps) * [Related resources](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#related-resources) * [Notes](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#notes) # Shop Pay Wallet API reference A list of the Shop Pay Wallet API endpoints and their parameters. Info Type signatures throughout use [GraphQL-style syntax](https://graphql.org/learn/schema/). The bang (`!`) operator represents a required, non-nullable field. Absence of the bang operator implies a nullable field. *** ## Authorizations Info The base URL for all authorization requests is `https://shop.app` ### Request authorization Authentication Webview /pay/authorize The entry point for a Shop Pay OAuth flow. The client should redirect the User to this endpoint and once there, the User will complete authorization on the `shop.app` domain. On redirect back, the client will receive an authorization code that can be used to request access and refresh tokens. When your client is authorized by a user, it is authorized for all Shopify merchants. In other words, authorization is between your client and the buyer and does not relate to any specific merchant. | | | | | - | - | - | | **Query Parameters** | | | | **Key** | **Type** | **Description** | | `response_type` | ``` String! ``` | The value of "code". | | `client_id` | ``` String! ``` | To be provided by Shop Pay. | | `scope` | ``` String! ``` | A space-delimited list of scopes. Refer to [Authorization scopes](https://shopify.dev/docs/api/shop-pay-wallet/scopes) for a list of allowed scopes. | | `redirect_uri` | ``` String! ``` | Client redirect URI, once the flow is complete. | | `login_hint` | ``` String ``` | You can speed up the authorization flow by telling Shop Pay about the customer. Shop Pay uses the `login_hint` to identify which customer is logging in. If you add the `login_hint`, and Shop Pay recognizes the `login_hint`, then the user doesn't need to provide their email and can skip ahead in the flow. The `login_hint` can be one of the following values:- The `user_uuid` of the customer (can be obtained from the Wallet or Order API) - The `user_email` of the customer | | `state` | ``` String ``` | An opaque value used by the client to maintain state between the request and callback. | | `channel` | ``` String ``` | For clients with multiple experiences, a stable identifier for the user to recognize the experience. | | **PKCE parameters** | As defined in PKCE OAuth Extension [RFC7636 Section 4.3](https://tools.ietf.org/html/rfc7636#section-4.3) | | | `code_challenge` | ``` String ``` | Derived from the code\_verifier, depending on the code\_challenge\_method below. | | `code_challenge_method` | ``` String ``` | Based on RFC7636 [Section 4.2](https://tools.ietf.org/html/rfc7636#section-4.2) and [Appendix A](https://tools.ietf.org/html/rfc7636#appendix-A)Method used to derive the code\_challenge from the code\_verifier. The only supported challenge method is `S256`: we expect a base64 encoded, SHA-256 hash digest of the code\_verifier, which is included when [requesting an Access Token](#request-an-access-token). | | **Example** | | | | cURL | Basic## Terminal```terminal curl --location --request GET 'https://shop.app/pay/authorize?response_type=code&client_id=ch4rl1nh0&scope=uma:pay:address:read uma:pay:credit_card:read uma:pay:credit_card:read_payment_session&redirect_uri=http://awesome-partner-app.io/pay/wallet/redirect&state=123456' \ ```with Login Hint```terminal curl --location --request GET 'https://shop.app/pay/authorize?response_type=code&client_id=ch4rl1nh0&scope=uma:pay:address:read uma:pay:credit_card:read uma:pay:credit_card:read_payment_session&redirect_uri=http://awesome-partner-app.io/pay/wallet/redirect&state=123456&login_hint=us3r-uu1d' \ ``` | | #### Responses | | | | | - | - | - | | **Type** | **Response Code** | **Details** | | Success Response | `302 Found` | `[redirect_uri]?code=&state=`Redirect with authorization info in the query params. Authorization codes are single use and expire after 5 minutes. If it takes more than 5 minutes to complete the request for authorization, you will have to restart the flow with the User. | | Error Response | `302 Found` | `[redirect_uri]?error=`Redirect with error info in the query params, where `error` is one of:- `invalid_request` - `unauthorized_client` - `access_denied` - `unsupported_response_type` - `invalid_scope`For more information on the authorization error response, see [RFC6749 section 4.1.2.1](https://tools.ietf.org/html/rfc6749#section-4.1.2.1). | | Error Response | `400 Bad request` | The error response will include information about the failure.**Example:**```text { "error": , "error_message": "Something is invalid" } ```The request isn't redirected when `error` is one of the following:- `invalid_request` - `invalid_client` - `invalid_grant` - `unauthorized_client` - `unsupported_grant_type` - `invalid_scope`For more information on authorization error responses, refer to [RFC6749 section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2). | *** ## Managing Tokens The base URL for all token requests is `https://accounts.shop.app` ### Request an access token For more information on the OAuth 2.0 Access token request pattern, see [RFC6749 Section 4.1.3](https://tools.ietf.org/html/rfc6749#section-4.1.3). POST /oauth/token You should exchange the authorization code you received in the previous step for an access token and refresh token. The access token is required to retrieve a buyer's wallet information using the [Wallet and Order](#wallet-and-orders) endpoints while a Refresh Token can only be used to generate a new access token. | | | | | - | - | - | | **HTTP Headers** | | | | `Authorization` | `Basic base64-encode(client_id:client_secret)`The word `Basic`, followed by a base64-encoded string of your `client_id` and `client_secret`, separated by a `:`. | | | `Content-Type` | `application/x-www-form-urlencoded` | | | `User-Agent` | A string identifying the service of origin making the request with optional runtime metadata. For example:`ShopPay/production Ruby/3.0.1` | | | **Request Body** | | | | **Key** | **Type** | **Description** | | `grant_type` | ``` String! ``` | The value of "authorization\_code". | | `redirect_uri` | ``` String! ``` | The redirect URI from the previous step. | | `code` | ``` String! ``` | The Authorization Code obtained in the previous step. | | **PKCE parameters** | As defined in PKCE OAuth Extension [RFC7636 Section 4.3](https://tools.ietf.org/html/rfc7636#section-4.3) | | | `code_verifier` | ``` String ``` | The code\_verifier value. | | **Example** | | | | cURL | ```terminal curl --request POST 'https://accounts.shop.app/oauth/token' \  --header 'User-Agent: ShopPay/production Ruby/3.0.1' \  --header 'Authorization: Basic Y2g0secretCodeOjEyM2FiYw==' \  --header 'Content-Type: application/x-www-form-urlencoded' \  --data-urlencode 'grant_type=authorization_code' \  --data-urlencode 'code=123authcode456' \  --data-urlencode 'redirect_uri=http://awesome-partner-app.io/pay/wallet/redirect' \  --data-urlencode 'code_verifier=Pkc3C0d3V3r1fi13r' ``` | | #### Responses | | | | - | - | | **Response Code** | **Details** | | `200 OK` | Success response with token info. | | `400 Bad Request` | The error response will include information about the failure.**Example:**```text { "error": , "error_message": "Something is invalid" } ```Where `error` is one of:- `invalid_request` - `invalid_client` - `invalid_grant` - `unauthorized_client` - `unsupported_grant_type` - `invalid_scope`For more information on the authorization error response, see [RFC6749 section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2). | | `401 Unauthorized` | If authorization with the provided client credentials fails. | | `403 Forbidden` | If we deny access to the resource. A common cause is a missing `User-Agent` header in the request. | #### Success response example ```JSON { "token_type": "Bearer", "expires_in": "3600", // Access Token TTL in seconds "access_token": "", // Access Token TTL is 1 hour "refresh_token": "" // Refresh Token TTL is 1 year } ``` ### Refresh an access token POST /oauth/token You can exchange the refresh token received during authorization for a new access token. This is done completely without user involvment as long as you have a valid refresh token. | | | | | - | - | - | | **HTTP Headers** | | | | `Authorization` | `Basic base64-encode(client_id:client_secret)`The word `Basic`, followed by a base64-encoded string of your `client_id` and `client_secret`, separated by a `:`. | | | `Content-Type` | `application/x-www-form-urlencoded` | | | `User-Agent` | A string identifying the service of origin making the request with optional runtime metadata. For example:`ShopPay/production Ruby/3.0.1` | | | **Request Body** | | | | **Key** | **Type** | **Description** | | `grant_type` | ``` String! ``` | The value of `refresh_token`. | | `refresh_token` | ``` String! ``` | Previously obtained refresh token. | | **Example** | | | | cURL | ```terminal curl --request POST 'https://accounts.shop.app/oauth/token' \  --header 'User-Agent: ShopPay/production Ruby/3.0.1' \  --header 'Authorization: Basic Y2g0cmwxbmgwOjEyM2FiYw==' \  --header 'Content-Type: application/x-www-form-urlencoded' \  --data-urlencode 'grant_type=refresh_token' \  --data-urlencode 'refresh_token=abcd12345efghi6789' ``` | | #### Responses | | | | - | - | | **Response Code** | **Details** | | `200 OK` | Success response with refreshed token info. | | `400 Bad Request` | The error response will include information about the failure.**Example:**```text { "error": , "error_message": "Something is invalid" } ```Where `error` is one of:- `invalid_request` - `invalid_client` - `invalid_grant` - `unauthorized_client` - `unsupported_grant_type` - `invalid_scope` - `invalid_refresh_token` (refer to the [FAQ](#what-should-i-do-when-the-refresh-token-expires))For more information on the authorization error response, see [RFC6749 section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2). | | `401 Unauthorized` | If authorization with the provided client credentials fails. | | `403 Forbidden` | If we deny access to the resource. A common cause is a missing `User-Agent` header in the request. | #### Success response example ```JSON { "access_token": "eyJ...MoQ", "expires_in": 86400, "scope": "", "token_type": "Bearer" } ``` ### Revoke an access token POST /oauth/revoke If the user signifies that they want to disconnect their Shop Pay accounts from the client. | | | | | - | - | - | | **HTTP Headers** | | | | `Authorization` | `Basic base64-encode(client_id:client_secret)`The word `Basic`, followed by a base64-encoded string of your `client_id` and `client_secret`, separated by a `:`. | | | `Content-Type` | `application/x-www-form-urlencoded` | | | `User-Agent` | A string identifying the service of origin making the request with optional runtime metadata. For example:`ShopPay/production Ruby/3.0.1` | | | **Request Body** | | | | **Key** | **Type** | **Description** | | `token` | ``` String! ``` | The string value of the token, either an access token or a refresh token. | | `token_type_hint` | ``` String! ``` | Valid values are `access_token` or `refresh_token`. | | **Example** | | | | cURL | ```terminal curl --location --request POST 'https://accounts.shop.app/oauth/revoke' \ --header 'User-Agent: ShopPay/production Ruby/3.0.1' \ --header 'Authorization: Basic Q2wxM250MUQ6MTIzYWJj' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=abcd12345efghi6789' \ --data-urlencode 'token_type_hint=access_token' ``` | | #### Responses | | | | - | - | | **Response Code** | **Details** | | `200 OK` | Success response when token is revoked successfully, or if the token was invalid anyway. For more information on the token revocation response, see [RFC7009 section 2.2](https://tools.ietf.org/html/rfc7009#section-2.2) | | `400 Bad Request` | The error response will include information about the failure.**Example:**```text { "error": , "error_message": "Something is invalid" } ```Where `error` is one of:- `invalid_request` - `invalid_client` - `invalid_grant` - `unauthorized_client` - `unsupported_grant_type` - `invalid_scope` - `unsupported_token_type` (see [RFC 7009 section 2.2.1](https://tools.ietf.org/html/rfc7009#section-2.2.1))For more information on the authorization error response, see [RFC6749 section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2). | | `401 Unauthorized` | If authorization with the provided client credentials fails. | | `403 Forbidden` | If we deny access to the resource. A common cause is a missing `User-Agent` header in the request. | ### Get access token information POST /oauth/introspect Obtain information about an access token or a refresh token. | | | | | - | - | - | | **HTTP Headers** | | | | `Authorization` | `Basic base64-encode(client_id:client_secret)`The word `Basic`, followed by a base64-encoded string of your `client_id` and `client_secret`, separated by a `:`. | | | `Content-Type` | `application/x-www-form-urlencoded` | | | `User-Agent` | A string identifying the service of origin making the request with optional runtime metadata. For example:`ShopPay/production Ruby/3.0.1` | | | **Request Body** | | | | **Key** | **Type** | **Description** | | `token` | ``` String! ``` | The string value of the token, either an access token or a refresh token. | | `token_type_hint` | ``` String! ``` | Valid values are `access_token` or `refresh_token`. | | **Example** | | | | cURL | ```terminal curl --location --request POST 'https://accounts.shop.app/oauth/introspect' \ --header 'User-Agent: ShopPay/production Ruby/3.0.1' \ --header 'Authorization: Basic Q2wxM250MUQ6MTIzYWJj' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=abcd12345efghi6789' \ --data-urlencode 'token_type_hint=access_token' ``` | | #### Responses | | | | - | - | | **Response Code** | **Details** | | `200 OK` | Success response when token is successfully introspected. | | `400 Bad Request` | The error response will include information about the failure.**Example:**```text { "error": , "error_message": "Something is invalid" } ```Where `error` is one of:- `invalid_request` - `invalid_client` - `invalid_grant` - `unauthorized_client` - `unsupported_grant_type` - `invalid_scope`For more information on the authorization error response, see [RFC6749 section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2). | | `401 Unauthorized` | If authorization with the provided client credentials fails. | | `403 Forbidden` | If we deny access to the resource. A common cause is a missing `User-Agent` header in the request. | #### Success response examples **When the token is still active:** ```JSON { "active": true, "exp": 1600171915, // Timestamp, in seconds "scope": "", "sub": "abc...123", // Authorizing user’s uuid. "username": "j••••e@shopify.com", // Authorizing user’s username. "token_type": "Bearer" } ``` **When the token is no longer valid:** ```JSON { "active": "false" } ``` ### Token FAQ #### What if the access token expires between the time it is granted and when it is required to retrieve a buyer's wallet information? In this case, as long as you have a valid refresh token, you can make a backend call to [refresh your access token](#refresh-an-access-token) and then use your new token in your subsequent requests. #### What should I do when the refresh token expires? After 1 year, the `refresh_token` expires and stops working. When the `refresh_token` expires, the `/token` endpoint returns a 400 (bad request) HTTP status, and an `invalid_refresh_token` error. If the endpoint returns a 400 (bad request) HTTP status, then the client needs to trigger a new [authorization request](#request-authorization) from the buyer. Re-authorization can be sped up by using the `user_uuid` or `user_email` for the `login_hint` query parameter. After triggering this lightweight authorization flow, a new authorization code is returned, with which you can obtain new refresh and access tokens. #### What if the buyer wants to change the card or address they authorized my client with? As part of their UI, the Client should offer to the user the option to change which Shop Pay card and shipping address are associated with the authorization (these items are represented later in the Wallet and Order API). To accomplish this, the client should perform the [Authorization Flow](#request-authorization) again, starting at the Get the Authorization Code step. The client will receive a new authorization code, which can be used to obtain a new refresh token and access tokens. These tokens will be associated with the updated data that the user has chosen. #### I will authorize with a buyer in order to sell them a product from a specific merchant.​Does that merchant own the token data generated during authorization? These tokens are not specific to any merchant, they do not belong to any merchant, and they do not contain any merchant owned data. The tokens we grant you may be used for your buyer, after they have authorized their consent, across all merchants. *** ## Wallet and Orders The base URL for all wallet and order requests is `https://shop.app` ### Get a user's wallet information GET /pay/wallet Obtain the user’s wallet information. Contains full shipping address and partial card information. | | | | - | - | | **HTTP Headers** | | | `Authorization` | `Bearer access_token`This is User-scoped authorization via an OAuth Access Token generated via the [OAuth authorization flow](#request-authorization). | | `User-Agent` | A string identifying the service of origin making the request with optional runtime metadata. For example:`ShopPay/production Ruby/3.0.1` | | **Example** | | | cURL | ```terminal curl --location --request GET 'https://shop.app/pay/wallet' \  --header 'User-Agent: ShopPay/production Ruby/3.0.1' \  --header 'Authorization: Bearer 123accesstoken456' ``` | #### Responses | | | | - | - | | **Response Code** | **Details** | | `200 OK` | Success response. See below for an example. | | `401 Unauthorized` | If authorization with the provided User credentials fails. | | `403 Forbidden` | If we deny access to the resource. A common cause is a missing `User-Agent` header in the request. | #### Success response example ```json { "user": { "uuid": "..." }, "card": { "uuid": "...", "lastFourDigits": "1111", "network": "VISA", // See note below "type": "CREDIT", // "DEBIT", "PREPAID", "UNKNOWN" "fingerprint": "v1:protected:vPzq0Ov10oj74TUHrSilp000jvlUNTVW5HrH7h9Wx83" }, "shippingAddress": { "uuid": "...", "country": "CA", "addressLine": [ "123 High Street" ], "region": "QC", "city": "Mont Royal", "dependentLocality": "", "postalCode": "H3B2Y5", "sortingCode": "", "organization": "", "recipient": "Jane Doe", "phone": "+15145551212" } } ``` Info Responses from this endpoint will provide the card network in upper case. See the [full list of supported card networks](https://github.com/activemerchant/active_merchant/blob/cfcd5bfc69d04747f6e59da2dc87c7288f90a2e6/lib/active_merchant/billing/credit_card_methods.rb#L7-L42) for more options. ### Confirm an Order and retrieve payment information POST /pay/wallet/orders This endpoint notifies Shop Pay that you will place an order with the Shopify Core APIs and it returns the information you need (payment token or encrypted PAN, credit card summary, billing and shipping addresses) to confirm an order with the user and to complete a payment and checkout with the Shopify Core Admin API. Calling this endpoint does not create a payment or checkout for you. See [Completing a Checkout](#completing-a-checkout) for more information on next steps. | | | | | - | - | - | | **HTTP Headers** | | | | `Authorization` | `Bearer access_token`This is User-scoped authorization via an OAuth Access Token generated via the [OAuth authorization flow](#request-authorization). We require User-scoped auth to confirm an Order so that we can associate the User and the Order in Shop Pay's systems. This association can only be set on creation and it cannot be changed. | | | `Content-Type` | `application/json` | | | `Certificate` | `Base64String`Base64-encoded string of the public certificate used for one-time encryption of the payment information returned in the API response. To learn how to generate an encryption certificate or for detailed instructions on formatting, refer to [Payment method encryption](https://shopify.dev/docs/api/shop-pay-wallet/encryption). This header is required if `tokenization_type` in the JSON Request Body is `PAYMENT_CREDENTIAL` and can be left out otherwise. | | | `User-Agent` | A string identifying the service of origin making the request with optional runtime metadata. For example:`ShopPay/production Ruby/3.0.1` | | | **JSON Request Body** | | | | **Key** | **Type** | **Description** | | `order_ids` | ``` [String!]! ``` | For all Order types, a list of order IDs from your internal systems.These `order_id`s are what we use to link your the Shop Pay interactions with the corresponding checkout on Shopify Core's Admin API. So the `order_id` you provide here must match the `source_identifier` you provide to the [checkout resource](https://shopify.dev/docs/api/admin-rest/2022-04/resources/checkout). These identifiers should uniquely identify the order on your system accross any merchant you work with. For instance, if you use identifer `123` for an order with Merchant A, you cannot use `123` on a later order, even with Merchant B. | | `tokenization_type` | ``` String! ``` | A specification of the tokenization method. One of the following allowed values:- `PAYMENT_CREDENTIAL`: request an encrypted network token or PAN. - `SESSION`: request a payment session ID. | | **Example** | | | | cURL | ```terminal curl --location --request POST 'https://shop.app/pay/wallet/orders' \  --header 'User-Agent: ShopPay/production Ruby/3.0.1' \  --header 'Certificate: b4s364Str1ng' \  --header 'Authorization: Bearer 123accesstoken456' \  --header 'Content-Type: application/json' \  --data-raw '{ "order_ids": ["1234"], "tokenization_type": "SESSION"  }' ``` | | #### Responses | | | | - | - | | **Response Code** | **Details** | | `200 OK` | Success response. See below for an example. | | `400 Bad Request` | Generic error when we fail to parse all or part of the request. | | `401 Unauthorized` | If authorization with the provided User credentials fails. | | `403 Forbidden` | If we deny access to the resource. Common causes include:- a missing `User-Agent` header in the request - asking for a `tokenization_type: PAYMENT_CREDENTIAL` when the scopes you requested in the [OAuth authorization flow](#request-authorization) only allow for `tokenization_type: SESSION`. | | `429 Too Many Requests` | Rate limit response. | #### Success response example The success response that's returned when confirming an order is the current wallet information tied to the buyer and order. The response includes a shipping address, billing address, credit card summary, and different payment information depending on the requested `tokenization_type`. **For `tokenization_type: PAYMENT_CREDENTIAL`** The response includes encrypted payment information needed to process payment for the order. For more details on how to decrypt this payload, see [Payment method encryption](https://shopify.dev/docs/api/shop-pay-wallet/encryption). ```json { "user": { "uuid": "..." }, "card": { "uuid": "...", "billingAddress": { "addressLine": [ "123 High Street" ], "city": "Mont Royal", "country": "CA", "dependentLocality": "", "organization": "", "phone": "+15145551212", "postalCode": "123456", "recipient": "Jane Doe", "region": "QC", "sortingCode": "" }, "lastFourDigits": "1111", "network": "VISA", // See note below "type": "CREDIT", // "DEBIT", "PREPAID", "UNKNOWN" "fingerprint": "v1:protected:vPzq0Ov10oj74TUHrSilp000jvlUNTVW5HrH7h9Wx83", "encryptedPayload": { "encryptedMessage": "x2H/6/2YH+xiemQnHRddR32c…=", "ephemeralPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n", "tag": "7HFdSX+9BLohm7YrYJy4qQ==" } }, "shippingAddress": { "uuid": "...", "country": "CA", "addressLine": [ "123 High Street" ], "region": "QC", "city": "Mont Royal", "dependentLocality": "", "postalCode": "12345", "sortingCode": "", "organization": "", "recipient": "Jane Doe", "phone": "+15145701612" } } ``` **For `tokenization_type: SESSION`** The response includes a payment session ID needed to process payment for the order. For more details on how to use a payment session ID to process a payment, see the [Creates a new payment API](https://shopify.dev/docs/api/admin-rest/latest/resources/payment.html). ```json { "user": { "uuid": "..." }, "card": { "uuid": "...", "billingAddress": { "addressLine": [ "123 High Street" ], "city": "Mont Royal", "country": "CA", "dependentLocality": "", "organization": "", "phone": "+15145551212", "postalCode": "123456", "recipient": "Jane Doe", "region": "QC", "sortingCode": "" }, "lastFourDigits": "1111", "network": "VISA", // See note below "type": "CREDIT", // "DEBIT", "PREPAID", "UNKNOWN" }, "payment": { "paymentSessionId": "west-a1584d23a51970fb8065ec71c34d25db" }, "shippingAddress": { "uuid": "...", "country": "CA", "addressLine": [ "123 High Street" ], "region": "QC", "city": "Mont Royal", "dependentLocality": "", "postalCode": "12345", "sortingCode": "", "organization": "", "recipient": "Jane Doe", "phone": "+15145701612" } } ``` Info Responses from this endpoint will provide the card network in upper case. See the [full list of supported card networks](https://github.com/activemerchant/active_merchant/blob/cfcd5bfc69d04747f6e59da2dc87c7288f90a2e6/lib/active_merchant/billing/credit_card_methods.rb#L7-L42) for more options. Unlike for the `tokenization_type: PAYMENT_CREDENTIAL` response, the `tokenization_type: SESSION` response will include neither an `encryptedPayload` nor a `fingerprint`. Instead, it includes a `paymentSessionId` that is accepted by the Sales Channel Payment resource. If you are working with non-Shopify merchants then continue with the step below to see how to update the Shop Pay Wallet over the course of the order's lifetime. If you are working with Shopify merchants, you can skip ahead to learn about [Completing a Checkout](#completing-a-checkout). ### Update an order This Endpoint is meant to be used by partners who want to provide Shop Pay to their buyers on non-Shopify merchants. It allows Shop Pay to capture all information about orders from the partner themselves instead of Shopify. PUT /pay/wallet/orders/:id Update the state of an existing Order. | | | | - | - | | **HTTP Headers** | | | `Authorization` | `Basic base64-encode(client_id:client_secret)`The word `Basic`, followed by a base64-encoded string of your `client_id` and `client_secret`, separated by a `:`. | | `Content-Type` | `application/json` | | `User-Agent` | A string identifying the service of origin making the request with optional runtime metadata. For example:`ShopPay/production Ruby/3.0.1` | | **Path Parameters** | | | `id` | The unique identifier of the Order in your system. One of the `order_ids` sent in the request to create an Order via `POST /pay/wallet/orders`. | | **JSON Request Body** | The current Order state. Each update is a reset of the Order state. The top-level JSON object should reflect the shape defined by the `Order` object.**Example:**```json { "cancelReason": null, "cancelledAt": null, "createdAt": "2021-01-01T00:00:00.000Z", "deliveryMethods": [], "fulfillments": [], "lineItems": [], "merchant": { "name": "Snowdevil", "url": "http://snowdevil.ca/" }, "paymentStatus": "PENDING", "refunds": [], "shipmentStatus": "UNSHIPPED", "total": { ... "totalPrice": { price: "0.00", currencyCode: "USD" } }, "updatedAt": "2021-01-01T00:00:00.000Z" } ``` | | **Example** | | | cURL | ```terminal curl --location --request POST 'https://shop.app/pay/wallet/orders/1234' \ --header 'User-Agent: ShopPay/production Ruby/3.0.1' \ --header 'Authorization: Basic Y2g0cmwxbmgwOjEyM2FiYw==' \ --header 'Content-Type: application/json' \ --data-raw ' { "total": { "totalTax": { "amount": "2.22", "currencyCode": "USD" }, "totalPrice": { "amount": "2.20", "currencyCode": "USD" }, "cartDiscounts": { "amount": "0.00", "currencyCode": "USD" }, "totalDiscounts": { "amount": "0.00", "currencyCode": "USD" }, "lineItemDiscounts": { "amount": "0.00", "currencyCode": "USD" }, "shippingDiscounts": { "amount": "0.00", "currencyCode": "USD" }, "lineItemSubtotalPrice": { "amount": "1.00", "currencyCode": "USD" }, "shippingSubtotalPrice": { "amount": "1.00", "currencyCode": "USD" } }, "refunds": [], "merchant": { "url": "https://snowdevil.ca/", "name": "Snowdevil", "merchantId": "315d97c3-aeb2-4e7b-bdf3-d0fd8e510607" }, "createdAt": "2021-08-10T13:51:05Z", "lineItems": [ { "gtin": "8102172c-0c6e-408b-b3fb-6646de1a52e8", "imageUrl": "https://example.com/image.jpg", "quantity": 1, "taxLines": [ { "price": { "amount": "0.10", "currencyCode": "USD" }, "title": "Line item sales tax" } ], "unitPrice": { "amount": "1.00", "currencyCode": "USD" }, "lineItemId": "85eb85fd-77f6-4493-9df6-dfa24a2f27b2", "productUrl": "https://example.com/snowboard", "totalPrice": { "amount": "1.00", "currencyCode": "USD" }, "description": "An example line item", "productTitle": "Snowboard", "originalUnitPrice": { "amount": "1.00", "currencyCode": "USD" }, "originalTotalPrice": { "amount": "1.00", "currencyCode": "USD" } } ], "updatedAt": "2021-08-10T13:51:05Z", "fulfillments": [ { "name": "Shipment", "status": "IN_TRANSIT", "inTransitAt": "2021-08-10T13:51:05Z", "lineItemIds": [ "47ce3714-8a3e-4fda-babe-f5f302d02b6d" ], "trackingInfo": { "carrierCode": "USPS", "trackingUrl": "https://example.com/tracking", "trackingNumber": "00594b99-b7c3-4454-9e56-b6dd4bcb2bc8" }, "fulfillmentId": "62144d7e-aef5-4132-bdc4-581cc2b4a4f2", "estimatedDeliveryAt": "2021-08-11T13:51:05Z" } ], "paymentStatus": "PAID", "shipmentStatus": "SHIPPED", "deliveryMethods": [ { "name": "Delivery purchase", "taxLines": [ { "price": { "amount": "0.10", "currencyCode": "USD" }, "title": "Shipment sales tax" } ], "unitPrice": { "amount": "1.00", "currencyCode": "USD" }, "totalPrice": { "amount": "1.00", "currencyCode": "USD" }, "lineItemIds": [ "47ce3714-8a3e-4fda-babe-f5f302d02b6d" ], "deliveryMethodId": "ccf15282-492d-4f2f-84c6-2819d65ea048", "originalUnitPrice": { "amount": "1.00", "currencyCode": "USD" }, "originalTotalPrice": { "amount": "1.00", "currencyCode": "USD" } } ] }' ``` | #### Usage This endpoint is meant to be called repeatedly over an Order's lifecycle. Each request should contain the total current set of data for the Order in the request body. We will reset the Order state on each update request. The Order state will reflect whatever data is sent in the most recent successful update request. This API design allows for incremental updates to an Order's state over its lifecycle via the following conventions: * Incrementally adding optional fields to fill in data as it becomes available. * Changing previously included data to update order state. * Omitting previously included data to delete order state. #### Responses | | | | - | - | | **Response Code** | **Details** | | `204 No Content` | Success response. | | `400 Bad Request` | Generic error when we fail to parse all or part of the request. | | `401 Unauthorized` | If authorization with the provided client credentials fails. | | `403 Forbidden` | If we deny access to the resource. A common cause is a missing `User-Agent` header in the request. | | `404 Not Found` | If an Order could not be found to update. | | `422 Unprocessable Entity` | If the update request parameters fail validation. The error response will include information about the validation failures.**Example:**```text { "errors": [ { "field": "createdAt", "code": "field_missing", "message": "Field createdAt cannot be null." } ] } ``` | | `429 Too Many Requests` | Rate limit response. | *** ## Order resources ### Delivery​Method A DeliveryMethod represents the purchase of a shipment for one or more `LineItems` in an `Order`. | | | | | - | - | - | | **Key** | **Type** | **Description** | | `deliveryMethodId` | ``` String! ``` | A stable identifier for the DeliveryMethod in your internal systems. | | `description` | ``` String ``` | Description of the purchased delivery. | | `lineItemIds` | ``` [String!]! ``` | The line items covered by this delivery.Each item in this array should map to one of the `lineItemIds` provided in your list of `LineItem` objects for the Order. | | `name` | ``` String! ``` | Name of the purchased delivery. | | `originalTotalPrice` | ``` Money! ``` | The total price of delivery for all the included line item units without any discounts or taxes applied.Computed as:``` originalUnitPrice * length(lineItemIds) ``` | | `originalUnitPrice` | ``` Money ``` | The delivery cost for a single line item unit without any discounts or taxes applied. | | `taxLines` | ``` [TaxLine!]! ``` | All taxes applied to the DeliveryMethod. | | `totalPrice` | ``` Money! ``` | The total price of delivery for all the included line item units with discounts applied. This price does not include taxes.Computed as:``` unitPrice * length(lineItemIds) ``` | | `unitPrice` | ``` Money ``` | The delivery cost for a single line item unit with discounts applied. This price does not include taxes. | ### Fulfillment A Fulfillment represents the shipment process via a delivery carrier for one or more `LineItems` in an `Order`. | | | | | - | - | - | | **Key** | **Type** | **Description** | | `deliveredAt` | ``` DateTime ``` | The time when the LineItems in the Fulfillment were delivered to the destination address. This field is always optional, since it may not be possible to capture the time of delivery within your systems.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | | `description` | ``` String ``` | Description of the Fulfillment. | | `estimatedDeliveryAt` | ``` DateTime ``` | The estimated delivery time for the Fulfillment as captured in your internal systems. This field is always optional, since it may not be possible to capture the estimated time of delivery within your systems.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | | `fulfilledAt` | ``` DateTime ``` | The time the Fulfillment is first received up by the delivery carrier as captured in your internal systems. This field is always optional, since it may not be possible to capture the fulfillment time within your systems.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | | `fulfillmentId` | ``` String! ``` | A stable identifier for the Fulfillment in your internal systems. | | `inTransitAt` | ``` DateTime ``` | The time the Fulfillment is put in transit by the delivery carrier as captured in your internal systems. This field is always optional, since it may not be possible to capture the in-transit time within your systems.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | | `lineItemIds` | ``` [String!]! ``` | The LineItems that will be delivered as part of this Fulfillment. Each item in this array should map to one of the `lineItemIds` provided in your list of `LineItem` objects for the Order. | | `name` | ``` String! ``` | Name of the Fulfillment. | | `status` | ``` String! ``` | The latest status of the Fulfillment as captured by your systems. One of the following allowed values:- `ATTEMPTED_DELIVERY` - `CANCELLED` - `CONFIRMED` - `DELIVERED` - `FAILURE` - `IN_TRANSIT` - `LABEL_PRINTED` - `LABEL_PURCHASED` - `LABEL_VOIDED` - `OUT_FOR_DELIVERY` - `PICKED_UP` - `READY_FOR_PICKUP` - `SUBMITTED` | | `trackingInfo` | ``` TrackingInfo ``` | Tracking data for the Fulfillment from the delivery carrier, if available. | ### Line​Item A LineItem represents the purchase and quantity of a product in an `Order`. | | | | | - | - | - | | **Key** | **Type** | **Description** | | `description` | ``` String ``` | Description of the product. | | `gtin` | ``` String! ``` | GTIN (Global Trade Item Number) or UPC code. | | `imageUrl` | ``` String! ``` | URL to an image thumbnail of the product. | | `lineItemId` | ``` String! ``` | A stable identifier for the line item from your internal systems. | | `originalTotalPrice` | ``` Money! ``` | The total price of all LineItem units without any discounts or taxes applied.Computed as:``` originalUnitPrice * quantity ``` | | `originalUnitPrice` | ``` Money! ``` | The price of a single LineItem unit without any discounts or taxes applied. | | `productTitle` | ``` String! ``` | Name of the product. | | `productUrl` | ``` String! ``` | URL linking to the product on the surface it was purchased. | | `quantity` | ``` Int! ``` | The quantity of this LineItem purchased. | | `taxLines` | ``` [TaxLine!]! ``` | All taxes applied to the LineItem. | | `totalPrice` | ``` Money! ``` | The total price of all LineItem units with discounts applied. This price does not include taxes.Computed as:``` unitPrice * quantity ``` | | `unitPrice` | ``` Money! ``` | The price of a single LineItem unit with discounts applied. This price does not include taxes. | | `variantTitle` | ``` String ``` | Optional modifier for the product title. For example: "Size: Medium". | ### Merchant | | | | | - | - | - | | **Key** | **Type** | **Description** | | `name` | ``` String! ``` | The name of the Merchant. | | `description` | ``` String ``` | A short description of the Merchant. | | `logoUrl` | ``` String ``` | A URL linking to the Merchant's logo. | | `merchantId` | ``` String! ``` | A stable identifier for the Merchant in your systems. | | `url` | ``` String! ``` | A URL linking to the Merchant's online surface where the Order was placed. | ### Money | | | | | - | - | - | | **Key** | **Type** | **Description** | | `amount` | ``` String! ``` | A decimal monetary value encoded as a String, where the decimal seperator is the `.` character. Example: `"1.50"` | | `currencyCode` | ``` String! ``` | Supported monetary currencies from ISO 4217. Example: `USD`. | ### Order | | | | | - | - | - | | **Key** | **Type** | **Description** | | `cancelReason` | ``` String ``` | The reason the Order was cancelled. One of the following allowed values:- `CUSTOMER` - `DECLINED` - `FRAUD` - `INVENTORY` - `OTHER` - `PARTIALLY_CANCELLED` | | `cancelledAt` | ``` DateTime ``` | The time when the Order was cancelled in your internal systems. Null if the Order is has not been cancelled.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | | `createdAt` | ``` DateTime! ``` | The time when the Order was created in your internal systems.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | | `deliveryMethods` | ``` [DeliveryMethod!]! ``` | A list of deliveries purchased as part of the Order. | | `fulfillments` | ``` [Fulfillment!]! ``` | A list shipment fulfillments via a delivery carrier for the items in the Order. Fulfillments do not need to map 1:1 with the DeliveryMethods. An Order can have a single DeliveryMethod split into multiple Fulfillments. | | `lineItems` | ``` [LineItem!]! ``` | A list of products and associated metadata for the Order. | | `merchant` | ``` Merchant! ``` | Info about the merchant associated with the Order. | | `orderNumber` | ``` String! ``` | The customer-facing identifier for the Order that will be included on communications such as confirmation pages and emails. | | `orderUrl` | ``` String ``` | A static link to a page on your platform where the customer can view details about the Order. | | `paymentStatus` | ``` String! ``` | The current status of payment for the Order. One of the following allowed values:- `AUTHORIZED` - `EXPIRED` - `PAID` - `PARTIALLY_PAID` - `PARTIALLY_REFUNDED` - `PENDING` - `REFUNDED` - `VOIDED` | | `refunds` | ``` [Refund!]! ``` | A list refunds applied to the Order. | | `shipmentStatus` | ``` String! ``` | The overall shipment status for the Order. This is an aggregate of the status for each `Shipment`. One of the following allowed values:- `UNSHIPPED` - `PARTIALLY_SHIPPED` - `SHIPPED` - `DELIVERED` | | `total` | ``` OrderTotal! ``` | Enumerated total cost of the Order. | | `updatedAt` | ``` DateTime! ``` | The time when the Order was last updated in your internal systems.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | ### Order​Total | | | | | - | - | - | | **Key** | **Type** | **Description** | | `cartDiscounts` | ``` Money! ``` | The total of any discounts applied to the Order itself, rather than a specific LineItem or DeliveryMethod. | | `lineItemDiscounts` | ``` Money! ``` | The total of all LineItem discounts in the Order. | | `lineItemSubtotalPrice` | ``` Money! ``` | The total cost of all LineItems in the Order.This includes LineItem discounts and Order-level discounts (`cartDiscounts`). This does not include any shipping costs or taxes.Computed as:``` sum(lineItems[].totalPrice) + cartDiscounts ``` | | `shippingDiscounts` | ``` Money! ``` | The total of all discounts for all DeliveryMethods in the Order. | | `shippingSubtotalPrice` | ``` Money! ``` | The total cost of all DeliveryMethods in the Order.This includes DeliveryMethod discounts. This does not include any Order-level discounts, line item costs, or taxes.Computed as:``` sum(deliveryMethods[].totalPrice) ``` | | `totalDiscounts` | ``` Money! ``` | The total of all discounts applied to the Order.Computed as:``` cartDiscounts + lineItemDiscounts + shippingDiscounts ``` | | `totalTax` | ``` Money! ``` | The total of all TaxLines for all LineItems and DeliveryMethods in the Order.Computed as:``` sum(lineItems[].taxLines[].price) + sum(deliveryMethods[].taxLines[].price) ``` | | `totalPrice` | ``` Money! ``` | The total cost of the Order, a combination of subtotal prices and taxes.Computed as:``` lineItemSubtotalPrice + shippingSubtotalPrice + totalTax ``` | ### Refund | | | | | - | - | - | | **Key** | **Type** | **Description** | | `createdAt` | ``` DateTime! ``` | The time when the Refund was created in your internal systems.An ISO-8601 encoded UTC datetime string. Example value: "2019-07-03T20:47:55Z". | | `description` | ``` String ``` | A short description of the Refund. | | `refundId` | ``` String! ``` | A stable identifier for the Refund in your internal systems. | | `refundLineItems` | ``` [RefundLineItem!]! ``` | The products and quantities of the Refund. | | `totalRefund` | ``` Money! ``` | The total amount of the Refund. | ### Refund​Line​Item | | | | | - | - | - | | **Key** | **Type** | **Description** | | `lineItemId` | ``` String! ``` | A reference to one of the line items in the Order. This should map to one of the `lineItemIds` provided in your list of `LineItem` objects. | | `quantity` | ``` Int ``` | The number of line items refunded. | ### Tax​Line | | | | | - | - | - | | **Key** | **Type** | **Description** | | `price` | ``` Money! ``` | The amount of the tax applied. | | `title` | ``` String! ``` | A description of the tax line. | ### Tracking​Info | | | | | - | - | - | | **Key** | **Type** | **Description** | | `carrierCode` | ``` String! ``` | Representing the carrier doing the shipment. | | `trackingNumber` | ``` String! ``` | Tracking number for the shipment. | | `trackingUrl` | ``` String ``` | Tracking URL for the shipment. | *** ## Completing a checkout Note Before moving to this step, ensure you have successfully [retrieved the buyer's payment information](#confirm-an-order-and-retrieve-payment-information) by confirming your order with the Shop Pay Wallet API. ### Retrieving `paymentSessionId` Depending on the `tokenization_type` you supplied when confirming an order and [retrieving the buyer's payment information](#confirm-an-order-and-retrieve-payment-information), your next steps will be slightly different. If you used `tokenization_type: SESSION` you have nothing else to do and may skip to [next steps](#next-steps). If you used `tokenization_type: PAYMENT_CREDENTIAL` and received an encrypted credit card number or network token, you will have to make a call to retrieve a `paymentSessionId` from our card vault. The payload you received contained encrypted payment information. If you haven't already, you will need to decrypt this payload, see [Payment method encryption](https://shopify.dev/docs/api/shop-pay-wallet/encryption). Once you have the decrypted credit card information, you can use it to [retrieve a payment session](https://shopify.dev/docs/api/admin-rest/unstable/resources/payment#get-checkouts-token-payments-payment-id). *** ## Next steps After confirming an order and retrieving a `paymentSessionId`, you have everything you need to create a [Checkout](https://shopify.dev/docs/api/admin-rest/2022-04/resources/checkout) and complete a [Payment](https://shopify.dev/docs/api/admin-rest/2022-04/resources/payment) using the Shopify Admin Sales Channel API. Specifically, you will need to provide the `order_id` (called `source_identifier` in the Checkout resource), `shippingAddress`,`billingAddress`, and `phone number` or `email` to the checkout resource. Then you will provide the `checkout_token` and the `paymentSessionId` to the payment resource. To help guide you through the rest of the flow we have answered a couple of common questions: **Orders? Checkouts? Payments?... So many resources, do I actually need to create them all?** No, you don't. You need to create a Checkout, then use that checkout token to complete a Payment. Upon Checkout completion, an Order will be created for you on behalf of the merchant in Shopify Core using the line items supplied when you created or updated the Checkout resource. **I have created a Checkout and completed a Payment. Do I need to also make a separate call to complete it?** Successfully completing a payment will close the associated Checkout, which will in turn create the Order on behalf of the merchant in Shopify Core. If the order is free, then completing a Payment is not necessary and you will need to complete the checkout with a separate call to the checkouts endpoint. *** ## Related resources * REST Admin API's [`Checkout`](https://shopify.dev/docs/api/admin-rest/latest/resources/checkout) resource * REST Admin API's [`Payment`](https://shopify.dev/docs/api/admin-rest/latest/resources/payment) resource * REST Admin API's [`Order`](https://shopify.dev/docs/api/admin-rest/latest/resources/order) resource * [Testing the Integration](https://shopify.dev/docs/api/shop-pay-wallet/testing) * [Shop Pay Wallet ecosystem](https://shopify.dev/docs/api/shop-pay-wallet/ecosystem) * [Track with Shop Link](https://shopify.dev/docs/api/shop-pay-wallet/track-with-shop) *** ## Notes #### Payment​Address interface The [PaymentAddress](https://www.w3.org/TR/payment-request/#paymentaddress-interface) interface used in the Wallet and Order APIs is obtained from the W3C Payment Request API. *** * [Authorizations](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#authorizations) * [Managing Tokens](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#managing-tokens) * [Wallet and Orders](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#wallet-and-orders) * [Order resources](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#order-resources) * [Completing a checkout](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#completing-a-checkout) * [Next steps](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#next-steps) * [Related resources](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#related-resources) * [Notes](https://shopify.dev/docs/api/shop-pay-wallet/reference/index#notes)