|
StripeKit 0.1.1
Stripe integration toolkit for C++
|
This page explains the handful of ideas you need, then walks through building a small subscription feature: a page where a customer signs up and pays, and code in your server that finds out once they have.
By the end you will have used the four pieces every StripeKit integration needs: a client to talk to Stripe, a Checkout session to take payment, a webhook handler to hear back from Stripe, and a store that stops that handler running twice for the same event.
This page uses the Drogon adapter, since that is the one StripeKit ships with. The examples/ directory in the source tree has runnable versions of everything shown here: CheckoutSessionCli and WebhookSignature are the same two things this page builds, DrogonCheckoutDemo puts them together into one small server, and BillingPortalCli shows the billing portal mentioned at the end.
Everything here uses Stripe test mode, which behaves exactly like the real thing but never touches real money. Test API keys start with sk_test_, and you should not have to think about "test vs. live" again until you are ready to go into production.
If you already know what Checkout, webhooks, and subscriptions are, skip ahead to Before you start.
Stripe hosts the payment page. Your application never touches a card number. Instead, you ask Stripe to create a "Checkout session", send the customer to the web page Stripe gives you, and Stripe takes care of collecting payment details safely.
Stripe tells you what happened by calling your server. When a customer pays, or a subscription renews, or a card is declined, Stripe sends an HTTP request to a URL you register — this is called a "webhook". Your server reads that request and reacts to it, for example by giving the customer access.
A subscription is an ongoing agreement to pay. A customer subscribes once, through Checkout, and Stripe then bills them automatically every month (or week, or year) until they cancel. StripeKit's Checkout support handles both recurring subscriptions (mode = "subscription") and one-time purchases (mode = "payment").
You need:
Keep the API key out of your source code. Put it in an environment variable instead, and read it from there:
The examples in the source tree read this same variable.
If you have not built StripeKit yet, see the "Build" section of the project README.md first.
Once it is built and installed, add it to your project's CMake file:
StripeKit::stripekit is the part described on this page: customers, Checkout, subscriptions, and webhooks. StripeKit::drogon is the adapter that lets those pieces talk to Stripe using the Drogon framework. If your application uses a different framework, link only StripeKit::stripekit and see Writing an Adapter for how to supply your own adapter.
Everything StripeKit does starts from a "client" object. Create one when your application starts, using the API key from earlier:
Create this once and reuse it for the whole lifetime of your application, rather than creating a new one for each request — it keeps its connection to Stripe open, so reusing it is both simpler and faster.
If your application is itself built on Drogon, add .event_loop = stripekit::drogon::EventLoopOptions::drogon_framework() to the options above, so StripeKit shares Drogon's own networking loop rather than starting a second one. This does not matter for a simple command-line tool like the one this page builds.
Before Stripe can bill someone, it needs a customer record for them. Create one the first time a person signs up in your application, and keep its id:
Store customer.id (something like cus_...) alongside that person's record in your own database. You only need to create the Stripe customer once per person, not every time they check out.
The metadata["app_user_id"] line is worth keeping. It is a free-form note you attach to the customer, and Stripe copies it onto related events later. That is how, when a webhook arrives, you can tell which of your users it is about.
Now create a Checkout session and send the customer to the web page it gives you:
session.url is a Stripe-hosted page where the customer enters their card details. Once they finish, Stripe sends them to success_url; if they give up, it sends them to cancel_url instead.
Do not treat reaching success_url as proof that the customer has paid — a customer can close their browser before the redirect happens, and the URL itself is not secret. Use it only to show a "thank you" message. The real confirmation that payment succeeded is the webhook covered next.
The CheckoutSessionCli example in the source tree is this same code as a complete, runnable program; DrogonCheckoutDemo shows it wired into an actual web page.
This is the step that actually matters: your server needs to hear from Stripe. Register a URL Stripe can call, and write the code that runs when it does:
A few things worth explaining here:
Behind the scenes, this checks the request really came from Stripe, checks whether it has been seen before, and only then runs your code above — so your handler is called exactly once per real event, no matter how many times Stripe delivers it.
If you ever need a piece of information this page's types do not include, event.payload_json holds the complete original data Stripe sent, as text.
The WebhookSignature example in the source tree is the smallest possible version of this: one webhook, nothing else.
Stripe cannot send requests to your laptop directly, so the Stripe CLI stands in for it during development. With your application running, in a separate terminal run:
This prints a secret starting with whsec_. Set that as STRIPE_WEBHOOK_SECRET before starting your application — it is the endpoint_secret from the code above. It is different from the secret your real, deployed endpoint will use later.
Leave that command running, and in another terminal, send a fake event:
Your handler should run once. Try the same command again: it should still only run once per event, which is the de-duplication working as intended.
Every call above accepts an optional second argument for settings that apply to just that one request:
Set idempotency_key on anything that creates something or charges money. If your network connection drops after Stripe has processed the request but before you see the reply, retrying with the same key tells Stripe "this is the same request, not a new one", and you get back the original result instead of creating a second subscription by accident. Build the key from something already unique in your own system, such as an order id.
If a call to Stripe fails, StripeKit throws an exception rather than returning an error code:
It is worth logging details.request_id. If you ever need Stripe's support, or want to look up exactly what happened in the Stripe dashboard, that id is how you find the specific request.
For the kind of subscription flow this page builds, these are the ones that matter:
| Event | What it means |
|---|---|
| checkout.session.completed | The customer finished Checkout and now has a subscription. |
| invoice.paid | A payment for a billing period succeeded. This is usually where you grant or extend access. |
| invoice.payment_failed | A payment failed. Stripe will automatically retry it a few times. |
| customer.subscription.updated | Something about the subscription changed, for example its plan or status. |
| customer.subscription.deleted | The subscription has ended. This is usually where you take access away. |
It is best to base access on invoice.paid and customer.subscription.deleted rather than on the Checkout redirect from step 3. Renewals happen automatically, weeks or months later, with no browser involved at all — the webhook is the only way to find out about them.
Stripe's own list of event types covers everything Stripe can send, including things outside subscription billing.
A few things to check before switching from a test key to a live one: