SD - Stripe and Shopify
How does Stripe (payment gateway) can trust incoming requests from the merchant backend like Shopify?
Two Approaches for Zero-Trust Security
- Using request signing with a shared secret key.
- Using OAuth 2.0.
In both approaches, we use JWT (JSON Web Token) to sign the request.
Approach 1: JWT-Signed API Request
Example of JWT-Signed API Request
- Browser → Merchant Backend:
POST /checkout
# request_body is the actual API payload to be sent to Stripe
request_body = {
"amount": 100,
"currency": "usd",
}
- Merchant Backend → Stripe (with JWT): Merchant generates a signed JWT using a shared secret key.
# jwt_payload is metadata used for authentication
jwt_payload = {
"request_body_hash": hash(request_body), # hash the request body
"nonce": UUID(), # unique request ID
"iat": current_time(), # current timestamp
"exp": current_time() + 10 minutes, # expiration timestamp
"merchant_id": "shopify_site_1_id", # merchant ID
}
- Generate signature using
# JWT header
jwt_header = {
"alg": "HS256",
"typ": "JWT"
}
# Encode header and payload
encoded_header = Base64UrlEncode(jwt_header)
encoded_payload = Base64UrlEncode(jwt_payload)
# Compute signature using shared secret
signature = HMAC_SHA256(
encoded_header + "." + encoded_payload,
shared_secret_key
)
- Final JWT token:
# Final JWT token
jwt_token = encoded_header + "." +
encoded_payload + "." +
Base64UrlEncode(signature)
- Send it to Stripe.
POST /stripe/charge
Authorization: Bearer <signed JWT>
{
"amount": 100,
"currency": "usd",
}
- Stripe → Merchant Backend (transaction_id):
- Verify JWT Signature: Stripe verifies the JWT using the shared secret key.
- Validate Claims:
iat
andexp
timestamps are valid.nonce
is a unique request ID for replay protection.body_hash
matches the actual body hash.
- Once validated, Stripe queues request and returns a transaction id to merchant
How does JWT work?
A JWT consists of three parts:
All three parts are base64 encoded and concatenated with a dot (.). Then sent as Authorization: Bearer token in each request from client to server.
ByteByteGo does a great job explaining JWT here.
TODO: Verify JWT using shared secret key vs public key
TODO: Merchant onboarding - How do you generate shared secret key?
TODO What's Projection DB? Why use it?
Listening Async Updates Using Webhooks
Once payment is processed, Stripe needs to send updates to merchant with transaction details via Webhooks.
- Merchant → Stripe (API Call) uses JWT Token
"Hey Stripe, I’m site1.com. Here’s my identity and proof (in this JWT token). You can check that I signed it correctly. Please go ahead and charge this card."
Why: Merchant is asking Stripe to take action, so merchant needs to prove who they are and that the request wasn’t tampered
- Stripe → Merchant (Webhook Callback) uses HMAC Signature
"Hey site1.com, I’m Stripe. Here's an event (like charge succeeded). You can check this HMAC signature to be sure it’s really from me and not tampered."
Why: Stripe is pushing an event, not requesting data, so merchant needs to verify the sender and check for tampering