Stripe technical guide
Everything an engineer needs to connect Stripe to Salesforce: architecture, the exact build steps with real code, field mapping, the data model, security, monitoring, and the pitfalls we design out.
The workhorse payment gateway across our Salesforce builds, from checkout and subscriptions to donations and reconciliation. We have shipped it across 16 client projects and 214 build tasks.
The value is what happens after the charge: matching payments to records, handling refunds, and keeping finance reconciled without manual work.
We build a hardened webhook pipeline: a public Apex REST endpoint on a Salesforce Site, signature verification on every event, and flows that turn raw Stripe events into clean, reconciled records.
Every Stripe build is delivered by a senior Salesforce architect on a fixed price, tested end to end in a sandbox, deployed to your org, and backed by 30 days of hypercare. You own the result: documented, source-controlled, and free of black-box middleware lock-in.
How Stripe connects to Salesforce
The real connection surface: how it authenticates, what it is built on, the endpoints and events in play, and where the reference docs live.
- Connects via
- Managed package (AppExchange): Stripe app for Salesforce PlatformPre-built Flow invocable actions plus Apex (AgnosticInvocable for uncovered endpoints)Inbound Stripe webhooks logged to a Stripe Events object, then processed in Flow
- Package
- Stripe app for Salesforce Platform
- Authentication
- Stripe secret / restricted API key (Bearer) outbound; inbound webhooks verified with an HMAC-SHA256 endpoint signing secret via the Stripe-Signature header
- API type
- REST+Webhooks
https://api.stripe.com/v1- Reference
- Official developer docs
Key endpoints
/v1/checkout/sessions/v1/payment_links/v1/charges/v1/payment_intents/v1/customersWebhook and platform events
checkout.session.completedcheckout.session.expiredcharge.succeededpayment_intent.succeededinvoice.paidBuild this with AI agents
Copy the full playbook as a prompt for any coding agent, or install it as a Claude Code skill. Either way your agent builds with our exact approach: the architecture, the Apex code, the field mapping, the rate limits, and the pitfalls to design out. Free, no signup.
Loading the Stripe playbook...What we build for a Stripe integration
Stripe wired into Salesforce across many orgs: pay-by-link buttons on Case and Account, Stripe webhook events handled in Flow, and an automated gift and donation capture that even creates Accounts for unknown donors.
Pay-by-link on the record
Stripe payment-link buttons on Case and Account and embedded in email templates, so staff collect payment without leaving Salesforce.
Webhook events handled in Flow
checkout.session.completed and charge.succeeded events captured and mapped to records with idempotent, replay-safe handling.
Gift and donation automation
A "Stripe Gift transaction" flow that creates gift and donation records, and creates Accounts for unknown donors on the fly.
Data hygiene and fixes
Diagnosed double-charge and event-mapping issues and backfilled payment methods that were missing a Stripe gateway.
Real components we ship
What you will need
What we confirm on both sides before writing a line of code.
From trigger to record, end to end
The production runtime flow, with what happens in each system.
- 01
Customer pays
In StripeA customer or donor pays through a Stripe payment link on a Case or Account, or a link embedded in an invoice or email.
$Card data stays in Stripe; Salesforce only ever sees tokens and ids, keeping PCI scope low. - 02
Stripe fires a webhook
In transitEvents such as checkout.session.completed and charge.succeeded post to a secured Apex REST endpoint.
$Delivered to a public /services/apexrest/ URL on a Salesforce Site. - 03
Verified and routed
In SalesforceThe controller verifies the signing secret, guards against duplicates and double-charges, and returns 200 fast.
$Signature validated with the webhook secret; processing continues async in a Queueable. - 04
Records created or updated
In SalesforceFlows create the payment or gift record and an Account for unknown donors, with Stripe ids stored.
$Idempotent upsert on the event id via SalesforceStripeWebhooksController. - 05
Reconcile and report
In SalesforceThe payment is reconciled to the Account or invoice, methods are backfilled, and refunds flow back.
$FetchAndUpdateStripePaymentBatch reconciles; RefundInvoiceFromStripeHelper handles refunds.
How the data actually flows
Left to right: sources, the integration layer, Salesforce, and the outcomes it drives.
// sources feed the integration layer, Salesforce persists, outcomes ship
The objects behind the integration
The Salesforce objects we read and write, what each one is for, and the fields that carry the load.
| Object | Purpose | Key fields |
|---|---|---|
Payment | The primary Salesforce record Stripe data maps onto. | External_Id__c, Name, Status |
Inbound_Event__c (custom) | Stores each raw event idempotently for audit and replay. | Event_Id__c, Payload__c, Processed__c |
Account | Matched or created for the customer or company behind the record. | Name, External_Id__c |
Error_Log__c (custom) | Captures every request, response, and failure so anything can be replayed. | Payload__c, Status__c, Related_Id__c |
Salesforce objects typically in play for Stripe
Build the Stripe integration
Every step we follow to ship a production-grade build, with the code that matters.
Plan the integration and prerequisites
We agree the events that matter and design a secure public endpoint before touching code.
- A Salesforce edition with API access, a dedicated sandbox, and a Salesforce Site for the public URL
- A Stripe account in test mode first, with admin access on both systems
- The exact events, target objects, and reconciliation rules agreed up front
- A hardened Site guest user with the absolute minimum permissions
Build the Apex REST endpoint
We give Stripe a typed, testable place to POST events.
- An @RestResource class with an @HttpPost handler mapped to a stable URL
- The raw request body is read once and kept for signature verification and audit
@RestResource(urlMapping='/webhook/*')
global with sharing class InboundWebhookResource {
@HttpPost
global static void handle() {
RestRequest req = RestContext.request;
String raw = req.requestBody.toString();
if (!WebhookSignature.isValid(raw, req.headers.get('X-Signature'))) {
RestContext.response.statusCode = 401; // reject unverified events
return;
}
Inbound_Event__c e = new Inbound_Event__c(
Event_Id__c = EventParser.idOf(raw), Payload__c = raw);
upsert e Event_Id__c; // idempotent capture
System.enqueueJob(new EventProcessor(e.Id)); // process asynchronously
RestContext.response.statusCode = 200; // respond fast
}
}Expose it securely on a Salesforce Site
We make the endpoint reachable without opening the whole org.
- Create a Salesforce Site and enable only the one Apex class for the guest user
- The public URL follows /services/apexrest/...; every other guest permission stays off
Watch out: lock down the guest user
A Salesforce Site runs as a guest user. Grant it access to only the one Apex class, or you expose far more of the org than a webhook ever should.
Register the webhook in Stripe
We subscribe to exactly the events we need, nothing more.
- In the Stripe dashboard, add the endpoint URL and select the relevant events (checkout, charge, invoice, refund, and so on)
- Copy the webhook signing secret into a protected custom setting or custom metadata
Verify signatures and prevent replay
We make sure only genuine, once-only events ever change data.
- Compute an HMAC-SHA256 of the raw body with the signing secret and constant-time compare it
- Reject on mismatch, and check the event timestamp to block replay attacks
public class WebhookSignature {
public static Boolean isValid(String rawBody, String header) {
Blob secret = Blob.valueOf(WebhookConfig.signingSecret());
Blob mac = Crypto.generateMac('HmacSHA256', Blob.valueOf(rawBody), secret);
String expected = EncodingUtil.convertToHex(mac);
// constant-time compare guards against timing attacks
return ConstantTime.equals(expected, header);
}
}Watch out: verify every event
A public endpoint is a target. Validate the signing secret and make the handler idempotent, or a retried or spoofed event can double-post to your records.
Respond fast, process asynchronously
We never let processing block the webhook response.
- Return HTTP 200 within the vendor timeout, which is usually only a few seconds
- Hand the heavy work to a Queueable so slow processing never triggers a retry storm
public class EventProcessor implements Queueable {
private Id eventId;
public EventProcessor(Id eventId) { this.eventId = eventId; }
public void execute(QueueableContext ctx) {
Inbound_Event__c e = [SELECT Payload__c FROM Inbound_Event__c WHERE Id = :eventId];
Map<String,Object> body = (Map<String,Object>) JSON.deserializeUntyped(e.Payload__c);
// heavy work runs here, off the webhook thread: map onto Accounts, Cases, Payments
EventRouter.route(body);
update new Inbound_Event__c(Id = eventId, Processed__c = true, Processed_At__c = System.now());
}
}Capture raw events idempotently
We keep a durable, replayable record of everything received.
- Upsert an Inbound_Event__c on the event id so duplicate deliveries are ignored
- Store the raw JSON for audit and for replay if a downstream mapping ever changes
Map events onto your Payment
We turn raw Stripe events into clean Salesforce data.
- Flows or triggers translate events onto Accounts, Payment, Payments, and Cases
- Store the Stripe ids on the records and handle out-of-order events gracefully
Handle money and edge cases
We cover the cases that otherwise become disputes.
- Reconcile payments, and handle refunds, chargebacks, and partial captures
- Backfill missing data and alert on any mismatch before it reaches finance
Test, deploy, and monitor
We prove it end to end and keep watch in production.
- Apex tests build a RestContext request and assert the resulting records; replay real test-mode events
- Deploy via change sets, restrict the events object to admins, and monitor with error logging plus 30 days of support
Example field mapping
How Stripe data lands on your Salesforce records. We tailor the full mapping to your org.
| Stripe | Salesforce | Notes |
|---|---|---|
| Stripe charge id | Payment.External_Id__c | Unique external id, upsert key |
| Stripe amount | Payment.Amount | |
| Stripe currency | Payment.CurrencyIsoCode | |
| Stripe customer | Account | Matched or created |
| Stripe status | Payment.Status | Picklist value mapping |
| Created / updated at | LastModifiedDate | Enables delta sync and audit |
| Owner or rep | Payment.OwnerId | Assignment rules or a default owner |
Rate limits and governor limits
The platform constraints we design around, so the integration stays fast and never falls over at scale.
Specific to Stripe
Salesforce platform limits
Secure by design
How we keep the integration safe, least-privilege, and compliant.
Monitoring, retries, and reliability
What keeps the integration trustworthy in production, and how you know the moment something needs attention.
How we test, deploy, and hand it over
The quality gates every build clears before it touches your production org.
Common pitfalls we design out
The mistakes that quietly break integrations, and how we avoid each one.
Missed or duplicated events
Verify signatures, upsert on the event id, and return 200 within the timeout.
Webhook times out on heavy processing
Acknowledge fast and process the event in a Queueable.
Guest user exposes too much
Grant the Site guest user access to only the single Apex class.
No visibility when it breaks
We log every call and surface failures on a dashboard with alerts, so an issue never goes unnoticed.
Reporting drifts from reality
External-id keys and a delta timestamp keep Salesforce and the source reconciled, so reports stay trustworthy.
Gotchas specific to Stripe
Stripe integration: technical FAQs
How do you authenticate Stripe with Salesforce?
We connect Stripe using named credentials and API keys and store every secret in Salesforce Named Credentials with a permission set, so nothing is hard-coded or shipped in metadata.
Does the Stripe integration handle bulk volume?
Yes. All Apex is bulkified, volume moves to Queueable or Batch Apex, and we respect the Salesforce governor limits (SOQL, DML, and callout caps per transaction).
How do you prevent duplicate records?
We upsert on a unique external-id field, so a retried or duplicate payload is idempotent and never creates a second Payment.
How is the integration tested and deployed?
Apex tests with HttpCalloutMock cover the success, failure, and a 200-record bulk case (75 percent plus coverage). We deploy via change sets or an SFDX and CI pipeline.
What happens if Stripe or Salesforce is briefly down?
Failed calls retry with backoff and land in an Error Log object with alerting, so nothing is lost and any event can be replayed.
How do you secure the webhook endpoint?
The Apex REST endpoint runs on a Salesforce Site with a locked-down guest user, verifies the HMAC signature on every event, and checks the timestamp to block replays.
Want us to build your Stripe integration?
Skip the build. In a free 30-minute call we will map your Stripe flow and hand you a clear, fixed-price plan.
