Stripe is a payment processing gateway that enables businesses to accept online payments by credit cards and other means. If you are a merchant already registered with Stripe, a simple way to integrate Stripe's service into your own website is to setup a Payment Link. Users clicking the payment link will be redirected to Stripe's website to make a payment before being redirected back to your website.
There are two ways to know if a successful payment is made:
-
Setup a webhook that allows Stripe to post a JSON message to your website, and it will tell you the payment amount and other details.
-
Find out the
checkout_session_id(orpayment_intent_id) associated with the user's payment, and call Stripe's web service API to lookup payment details.
method 1 requires you to verify a signature (embedded in the stripe-signature header) of the message using a master secret key,
while method 2 only requires a read-only key of limited scope.
They both have downsides:
-
Method 1 is less secure because the master key is stored in a canister's heap memory, and if anyone manages to obtain this key, they can forge
stripe-signaturefor fake payments. -
Method 2 requires canisters making HTTPs outcalls to an idempotent proxy that supports IPv6, because api.stripe.com only supports IPv4.
This approach is implemented by cycle.express, a website that accepts credit card payments to help you top-up canister cycles.
Here is a pointer to the code snippets that verify stripe signature.
This approach is implemented by this repository.
For local deployment:
- Make sure dfx has been started locally. If not run
dfx start --background
- Prepare dependency packages
npm install
export PATH=$PWD/node_modules/.bin:$PATH
mops install
- Create an account with Stripe if not already registered.
- Create a testing payment link in Stripe.
In "After payment" tab, choose "Don't show confirmation page", and fill in a redirection link
http://localhost:8080/?check_out_session_id={CHECKOUT_SESSION_ID} - After the link is created, append the URL to file
.envin this repository's directory like this (replacexxxxwith your actual link id)
echo STRIPE_PAYMENT_LINK=https://buy.stripe.com/test_xxxx >> .env
- Create a restricted key in Stripe that has read access to "Checkout Sessions".
- Deploy the backend canister with init argument (fill in the key created in the step above):
dfx deploy stripe_backend --argument '(record { api_host = "api.stripe.com"; api_key = "..." })'
- Run the frontend as a webpack dev server. By default it runs on port 8080.
npm run dev
- Visit frontend URL http://localhost:8080/ in a browser.
Basic workflow is:
- User clicks the pay button, the frontend generates a random
client_reference_idand redirects the user to the Stripe payment web page with this id. - User finishes payment on Stripe's page.
- Stripe redirects user back to localhost with a
checkout_session_id. - Frontend calls backend canister via a HTTP endpoint and pass it the
checkout_session_id. - Backend upgrades the query call to upgrade call, and makes a HTTPs outcall to
api.stripe.comto fetch data. - Frontend keeps polling backend until backend gets data from stripe and returns it to frontend.
Besides passing flag --ic to dfx commands, you'll need to change the payment link redirection to point to the frontend canister URL on IC (c.f. the output of the command below).
echo "https://$(dfx canister id stripe_frontend --ic).icp0.io/?check_out_session_id={CHECKOUT_SESSION_ID}"
Also, the backend canister needs be configured to use a proxy to call Stripe because Stripe doesn't support IPv6 yet.
This can be done by deploying the backend canister with an optional idemponent_proxy argument.
If you haven't deployed the idempotent proxy yourself, you can use the one provided by its author, as shown below:
dfx deploy --ic stripe_backend --argument '(record { \
api_host = "api.stripe.com"; \
api_key = "..."; \
idempotent_proxy = opt "idempotent-proxy-cf-worker.zensh.workers.dev" })'