Payment method encryption
The Shop Pay Wallet will return plain (untokenized) credit card information for use in processing payments. This will consist of:
- Primary Account Number (PAN) (i.e. the card number)
- Full name on the card
- Expiry month and year
- The card brand
This setup allows your integration to be used for placing orders with both Shopify and non-Shopify merchants on an external surface, since the information returned can be used within any payment processor to extract payment. But, for the same reason, it also presents a significant security risk.
Your requests to our API need to occur via an encrypted SSL/TLS HTTP connection, which ensures a layer of security. But, in order to further mitigate the risks of passing raw PANs over the wire, we have added an additional layer of encryption to protect this data. We use an integrated encryption process based on the Elliptic Curve Integrated Encryption Scheme (ECIES) to additionally encrypt the information required to process payment.
With this setup, we guarantee the following:
- All requests between your systems and the Shop Pay Wallet are done using a secure connection
- We'll only return encrypted payment information if we can verify your system's identity
- We'll never return unencrypted payment information to you
- Only your system that requested payment information from the Shop Pay Wallet will be able to decrypt the payload we provide
This guide explains how to work with this payment method encryption setup. This guide uses OpenSSL and Ruby in its examples.
Encryption setup
Anchor link to section titled "Encryption setup"This section details setup steps you must take to request and decrypt encrypted payment information from the Shop Pay Wallet. You will need to work with a Shop team member for part of this.
The end result of this setup work will be:
- A private encryption key known only to you, which your systems will use to decrypt and verify the payload
- A PEM-formatted public certificate signed by Shopify's Certificate Authority (CA), which Shop Pay will use to encrypt the payload
Step 1: Generate a public/private key-pair and CSR
Anchor link to section titled "Step 1: Generate a public/private key-pair and CSR"You'll start by generating a public/private elliptic curve (EC) keypair using the NIST P-256 method, which is implemented as the prime256v1
extension in OpenSSL. You'll use both keys to additionally generate a Certificate Signing Request (CSR), which will later be exchanged with Shopify for a PEM-formatted public certificate used in encryption requests. You'll also later use the private key during the decryption process.
It's important that the CSR has a complete Subject and that the Subject's CN is formatted as Shopify Payments Partner - {partner_name}
.
You can generate a CSR that has a shopping platform named Foobar Shopping
, which is maintained by Company, Inc.
, and headquartered in CA
(Canada), ON
(Ontario), Ottawa
, using the following Ruby code or the openssl
shell command:
The methods above, for example, create an EC private key file and a CSR file with the following contents:
You can introspect these two credentials to assert that the CSR signing process was successful. When you introspect the private key, you should see a pub
and priv
value:
Then, if you introspect the CSR, you should see:
- The same
pub
value encoded into the CSR - The full Subject string
Before moving on to the next step, you must preserve the private key in a secure location. You will later use the private key to decrypt and verify encrypted payment payloads.
Private key formatting
Anchor link to section titled "Private key formatting"OpenSSL and elliptic curve encryption support multiple formats for private keys. In the examples above, we generate and use a private key using Elliptic Curve (EC) format. You can use EC private keys only for elliptic curve encryption. Private keys in the EC format have the following PEM-file header:
Another commonly used private key format is PKCS8 formatting. PKCS8 is a generic formatting, meaning private keys in this format work with multiple encryption schemes. These private keys are lengthier when encoded into a PEM-file, and have a different PEM-file header:
You can use either of these two formats for the private key you generate for the Shop Pay Wallet ECIES. We recognize that some teams have strong opinions on private key formats. If you wish to use PKCS8 formatting specifically, you can convert an EC formatted private key PEM-file into a PKCS8 formatted PEM-file using the following OpenSSL command:
If we were to convert our example EC formatted private key from above into a PKCS8 formatted key, it should look like the following:
You need to convert your EC private key to a PKCS8 private key before signing your CSR with your private key.
Private key signing bytes
Anchor link to section titled "Private key signing bytes"In the examples above, we are using a private key without a signing byte.
Specific versions of OpenSSL preprend a "signing byte" to private key values. The signing byte indicates the private key version. The signing byte is not actually part of the private key, but rather encoded into the PEM-file and included in the introspected textual output of the private key in OpenSSL commands.
As a comparative example, here is an EC formatted private key encoded with a "signing byte" in a PEM-file:
And here is that same private key introspected into a textual output. Notice the 00:
prefixing the private key text.
Similar to how we support both EC and PKCS8 private key formats, you can use private keys with or without a signing byte encoded.
Step 2: Joint validation of the CSR
Anchor link to section titled "Step 2: Joint validation of the CSR"Next, you should share the CSR PEM-file with Shopify. This should be done via a secure channel, such as company email.
Before you generate a PEM-formatted public certificate signed by Shopify's CA, both parties must jointly verify the CSR's authenticity using a separate communications platform. You should verify the CSR's authenticity on a separate communication platform to ensure that the CSR wasn't tampered with when the CSR was shared with Shopify.
You should generate a fingerprint of the CSR file, so you can verify that you and a Shopify team member have generated identical CSR fingerprints. For example, you can use Ruby or the openssl
shell command to generate a fingerprint of the CSR:
Step 3: Shopify generates a public certificate
Anchor link to section titled "Step 3: Shopify generates a public certificate"Next, a Shopify team member will use your CSR to generate a public certificate signed by Shopify's Certificate Authority. They will share it back with you in a PEM-file.
The following text is an example of a PEM-formatted public certificate:
You can also introspect the public certificate to validate that it's encoded with the expected metadata. In the example above, the introspected public certificate has the following contents:
- The private key's
pub
fingerprint under theSubject Public Key Info
section. - The CSR's Subject CN value (
Shopify Payments Partner - Foobar Shopping
). - An
Issuer
that identifies Shopify's CA. In the examples below, the CA is from Shopify's staging environment. In production, the CA signature looks different.
Generating the Encryption Certificate HTTP header
Anchor link to section titled "Generating the Encryption Certificate HTTP header"Before you confirm an order with Shop Pay to receive the encrypted credit card information, you'll need to ensure that
the HTTP request's Certificate
header for the Confirm an order and retrieve payment endpoint is using the correct format.
When you make an HTTP request to this endpoint, you need to include the public certificate using one of the following methods:
- Encode the PEM-formatted public certificate as a Base64-encoded string in an HTTP request header named
Certificate
- Send the PEM-formatted public certificate body that has the newlines stripped from it in an HTTP request header named
Certificate
The following text is an example of a PEM-formatted public certificate:
You can encode the PEM-formatted public certificate as Base64, and then use the Base64-encoded, PEM-formatted public certificate as your HTTP request's Certificate
header.
To encode the PEM-formatted public certificate as Base64, you can use the base64
shell command:
You can use the body of the PEM-formatted public certificate with newlines removed as the HTTP request's Certificate
header. For example, to remove newlines from the Base64-encoded body of the PEM-formatted public certificate, and output the result to STDOUT, you can run the following shell command:
Retrieving encrypted credit card information
Anchor link to section titled "Retrieving encrypted credit card information"After finishing the steps above, you can then request encrypted credit card information and decrypt it when retrieved, using the Confirm an order endpoint.
Shopify uses the PEM-formatted public certificate included in the HTTP request headers to generate the encrypted credit card payload. If the request is successful, then the API response is returned in the following format:
The card.encryptedPayload
blob contains the encrypted credit card information, along with all the data you need to decrypt it! It can be documented as follows:
Attribute | Type | Description |
---|---|---|
encryptedMessage | Base64String |
A base64-encoded, encrypted, JSON-formatted string of the credit card data. |
ephemeralPublicKey | String (PEM) |
A plaintext, PEM-formatted string that is used to derive: 1. A shared secret for decrypting the encryptedMessage 2. An HMAC key used for verifying the tag This attribute is labeled "ephemeral" as it will be unique to each encrypted payload. |
tag | Base64String |
A base64-encoded HMAC tag used to verify the authenticity of the payload. |
You now have all the tools needed to decrypt and verify the payment information. This will involve the following steps:
- Deriving a shared secret using the private key and the
ephemeralPublicKey
- Expanding the shared secret to find the encryption key and the HMAC key
- Using the HMAC key to verify the encrypted payload authenticity
- Using the encryption key to decrypt the
encryptedMessage
Let's run through what this will look like:
The last line should print out a JSON blob of the decrypted credit card info. Our decrypted example response should look as follows: