Your site has to take payments, push leads into a CRM or read stock from an ERP, and you need to decide who builds that link. The choice matters more than the platform underneath, because an integration touches money and customer data every day it runs. This guide helps you pick the right payment gateway integration developer and check their work before you sign.
The short answer: four kinds of builder can do the job (an in-house engineer, a freelancer, an integration agency or a platform connector app). The right one depends on your payment model, the number of systems involved and who will maintain the code after launch.
It is written for CTOs, engineering leads and technical founders who want to score a candidate against evidence, not a pitch. You get the criteria that separate a safe integration from a fragile one, working code to ask a candidate to explain, and honest ranges for effort.
Key takeaways
- Choose the payment model (hosted, embedded or custom) before you choose the builder, because it sets your PCI scope and most of the effort.
- A safe payment integration verifies every webhook signature, stores each event ID and fulfils orders from the verified event, never from the success page.
- Keep API keys, provider accounts and the repository in your name from day one, and ask for runbooks at handover.
- Compare quotes by their scope drivers (payment methods, refunds, taxes, field mapping, legacy API quality), not by the headline number.
- Eight questions, listed near the end, expose inexperience in a single call.
Who can integrate your website with third-party APIs
Four kinds of builder can connect your website to a payment gateway, a CRM or an ERP. Each suits a different situation, and each carries one risk you can check before you commit. Find your situation first, then read the criteria that apply to every option.
Freelancer, agency, in-house or connector app
The cards below place each option by what it suits and what to verify.
Team you already have
In-house engineer
Suits a product you will change for years. Check who covers when that person is away and whether a second engineer reviews payment code.
One bounded job
Freelancer
Suits a single gateway or a single CRM sync. Check availability after launch and ask for a written handover.
Several systems
Integration agency
Suits payments plus CRM plus ERP that must agree with each other. Check that code, accounts and keys end up in your name.
Standard flows
Connector app
Suits common flows on a platform such as Shopify. Check the limits on custom logic, field mapping and monthly fees.
Whichever you choose, the build is a bridge between your site and systems you do not control. Provider outages, API changes and duplicate messages are normal on that bridge, so the builder's job is to make them harmless.
What each option is good at
An in-house engineer knows your product and your data best. That pays off when the integration changes often, for example when pricing rules or sales stages evolve every quarter. The cost is that one person often holds all the context.
A freelancer is fast and direct for a bounded job. A freelancer can be a sound choice for a single Stripe integration for your website when you have an engineer who can review the code. Without a reviewer, you trust one pair of eyes with your revenue path.
An integration agency brings review, several engineers and cover for holidays. That helps when payments, a CRM and an ERP must agree with each other. A connector app is the quickest route when your flow is standard. It stops being enough when you need rules the app does not expose.
The three jobs hidden in one request
A request such as 'connect Stripe to the site' hides three jobs. A good proposal prices all three:
- The happy path: the buyer pays and the order is confirmed.
- The failure paths: declines, refunds, disputes, duplicate messages and provider outages.
- The operations: monitoring, reconciliation, key rotation and updates when the provider changes its API.
A quote that covers only the first job looks cheap and ends up costing more. Most of the engineering judgment you pay for sits in jobs two and three.
Pick a payment model and PCI scope
Choose the payment model before you choose the builder. It decides how much card data your systems touch, which sets your PCI DSS scope and the compliance work that follows. PCI DSS is the card industry's security standard, maintained by the PCI Security Standards Council, and your provider and acquiring bank tell you which self-assessment questionnaire applies to you.
Payment models and PCI scope
- 01Buyer enters cardThe choice of model starts here
- AHosted checkoutNarrow PCI scope
- Provider pageCard goes straight to the provider
- Result and tokenAll your server receives
- BEmbedded fieldsSmall PCI scope
- Provider fieldsYour page only hosts them
- ProviderCard never reaches your server
- CCustom handlingWidest PCI scope
- Your serverCard passes through your systems
- ProviderReceives the card from you
Diagram of three payment models showing where a card number touches your server and how PCI scope widens from hosted to custom
The diagram shows where a card number travels in each model. The further it enters your own systems, the wider your scope, and the more your team has to prove.
Our pick to start
Hosted checkout
- Card data path: entered on the provider's page
- PCI effort: the lightest
- Design control: branding options only
- Build effort: days to a couple of weeks
When design pays off
Embedded fields
- Card data path: provider fields inside your page
- PCI effort: moderate, your page is in scope
- Design control: full layout control
- Build effort: moderate
Rarely justified
Custom card handling
- Card data path: through your own servers
- PCI effort: the heaviest, with audits
- Design control: total
- Build effort: highest, plus ongoing cost
Hosted checkout: smallest scope
Your buyer pays on a page the provider hosts, then returns to your site. Card data never touches your servers, so the compliance work is the lightest. Stripe Checkout, PayPal and Mercado Pago all offer this model, and a team can ship it in days. Choose it when speed and a small scope matter more than a fully branded payment step.
Embedded fields: more control, moderate scope
Provider-hosted fields sit inside your own page, so the layout feels like yours while the card data still goes straight to the provider. Stripe's Payment Element and similar components work like this. Your scope grows because your page loads the provider's script, so you must protect that page from tampering. Pick it when checkout design clearly affects your conversion.
Custom card handling: rarely worth it
In a custom build, card numbers pass through your servers. You then carry the widest scope, with audits, network controls and logging rules. Few online stores gain enough to justify it. If a candidate proposes it as the default, ask which requirement forces it.
What to ask about your PCI questionnaire
Ask the candidate which model they propose and which questionnaire it implies for you. A confident answer names the model, says what stays on your side (for example, protecting the checkout page and keeping the provider's script current) and points to your provider's guidance. Be careful with anyone who says 'PCI is handled' without naming the model.
The same questions apply to Adyen, PayPal, Mercado Pago and Redsys, which follow the same patterns. For local methods and regional gateways, the post on web development for fintech and digital payments in LATAM adds context.
Webhooks, retries and idempotency that protect revenue
Fulfil orders from a verified provider event, never from the page the buyer lands on. A webhook is a message the provider sends to your server when something happens, such as a payment succeeding. Done well, it keeps your orders correct when networks fail, buyers close tabs and providers retry.
Why it matters
The browser redirect is not proof of payment. A buyer can close the tab before the success page loads, or open that URL without paying. Fulfil orders from the verified webhook event.
This is the lifecycle to expect for every payment event: verify, deduplicate, acknowledge, process and reconcile.
Webhook lifecycle
- 01Provider sends eventPayment succeeded, signed by provider
- 02Verify signatureChecked against the raw body
- 03Look up event IDRepeat events are skipped
- 04Store ID, reply 200Provider stops retrying
- 05Worker fulfils orderRuns apart from the reply
- 06Order confirmedNightly check catches any gap
Diagram of the webhook lifecycle: verify signature, check event ID, acknowledge, process by worker, skip duplicates and reconcile nightly
Verify the signature on the raw body
Anyone can post JSON to a public URL. The signature header lets your server prove that the message came from the provider. It is computed over the exact bytes sent, so the route must read the raw body before any JSON parsing. A common bug is a global JSON parser that changes the bytes and makes every check fail, and the usual wrong fix is switching the check off. The Stripe webhooks documentation describes the scheme in full.
The sample below uses TypeScript, Express, the official stripe package and PostgreSQL. The pattern is the same for other providers.
import express from 'express';
import Stripe from 'stripe';
import { Pool } from 'pg';
// Schema (run once):
// CREATE TABLE processed_events (event_id text PRIMARY KEY, type text NOT NULL, received_at timestamptz NOT NULL DEFAULT now());
// CREATE TABLE jobs (id bigserial PRIMARY KEY, kind text NOT NULL, payload jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now());
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET!;
const db = new Pool({ connectionString: process.env.DATABASE_URL });
const app = express();
// The raw body is required: the signature covers the exact bytes Stripe sent.
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'] as string,
endpointSecret
);
} catch {
// Bad signature or altered body: reject and do nothing else.
return res.status(400).send('Invalid signature');
}
const client = await db.connect();
try {
await client.query('BEGIN');
// The primary key on event_id turns a duplicate delivery into a no-op.
const inserted = await client.query(
'INSERT INTO processed_events (event_id, type) VALUES ($1, $2) ON CONFLICT (event_id) DO NOTHING',
[event.id, event.type]
);
if (inserted.rowCount === 0) {
await client.query('ROLLBACK');
return res.status(200).send('Duplicate ignored');
}
// Queue the slow work for a worker instead of doing it inside the request.
if (event.type === 'payment_intent.succeeded') {
const intent = event.data.object as Stripe.PaymentIntent;
await client.query('INSERT INTO jobs (kind, payload) VALUES ($1, $2)', [
'fulfil_order',
JSON.stringify({ orderId: intent.metadata.order_id }),
]);
}
await client.query('COMMIT');
return res.status(200).send('ok'); // Answer fast.
} catch (err) {
await client.query('ROLLBACK');
return res.status(500).send('Retry later'); // The provider will retry.
} finally {
client.release();
}
});
// Outgoing request: the same key always returns the same result,
// so a retry after a timeout cannot create a second charge.
export async function createPayment(orderId: string, amountCents: number) {
return stripe.paymentIntents.create(
{ amount: amountCents, currency: 'usd', metadata: { order_id: orderId } },
{ idempotencyKey: `order-${orderId}-payment` }
);
}
app.listen(3000);
Store the event ID to ignore duplicates
Providers deliver at least once, so the same event can arrive twice. The handler inserts the event ID into a table with a unique constraint. If the insert conflicts, the event is a duplicate and the handler acknowledges it without repeating the work.
In the sample, the insert and the job creation share one transaction, so a crash cannot leave an event marked as processed with no job. The handler answers 200 quickly and a worker does the slow part, such as sending the order to your ERP. If you also process events with automation, the post on webhooks and AI integrations as data pipelines shows how to design the next step.
Idempotency keys on outgoing requests
Duplicates also happen in the other direction. A timeout leaves you unsure whether a charge went through. An idempotency key is a unique value you send with a request, so the provider returns the original result instead of creating a second charge.
Derive the key from something stable, such as the order ID, as the createPayment function above does. Stripe documents how long it keeps keys and what happens when the same key arrives with different parameters.
Reconcile against the provider daily
Webhooks can still be missed, for example during your own outage. A nightly job lists the provider's payments for the last day and compares them with your orders. Anything paid at the provider and missing in your store gets flagged or repaired automatically.
Ask a candidate how they would detect a missed event. If the answer is 'the customer would write to us', the design is incomplete.
API authentication, rate limits and monitoring
The same care applies to CRM, ERP and shipping APIs, where the usual risk is a leaked key, a broken sync or a silent failure rather than a card breach. A reviewer should recognise the categories in the OWASP API Security project, especially broken authorization and unrestricted resource consumption.
Keep secrets and scopes tight
Secrets belong in environment variables or a secrets manager, never in the repository, a front-end bundle or a chat message. Use separate keys for test and live, and give each key only the permissions it needs. Stripe's restricted keys and OAuth scopes on a CRM both allow this. If a key leaks, a narrow scope limits the damage and rotation is a routine task.
Check also that your server decides who may read which record. Broken object-level authorization, where one customer reads another's data by changing an ID, appears first in the OWASP API Security Top 10.
Backoff and queues for rate limits
Every API limits how fast you can call it. When you pass the limit, the API answers with HTTP 429 and often a Retry-After header. A good integration waits, retries with exponential backoff and random jitter, and puts non-urgent work in a queue so a burst of orders cannot flood the API.
The same patterns apply to AI providers, as the post on handling LLM API rate limits in production shows. Ask the candidate what happens to 5,000 queued updates after a one-hour outage. A good answer covers ordering, retries and a limit on how fast the backlog drains.
Alerts that reach a person
Silent failure costs the most: orders that never reach the ERP, leads that never reach the CRM. Monitoring should track failed jobs, webhook error rates and the age of the oldest queued item, then alert a named person or channel. A dashboard nobody opens does not protect you. Agree up front who receives the alert, what counts as urgent and how fast someone answers.
Cost and timeline of a payment gateway integration
Quotes vary widely because scope varies widely. The ranges below are illustrative, drawn from typical scoping conversations rather than a survey or a price list. Use them to sanity-check a quote, then ask the builder to show which drivers explain their number.
| Integration | Illustrative timeline | Main drivers |
|---|---|---|
| Hosted checkout | About 1 to 2 weeks | One gateway, few payment methods, simple refunds |
| Embedded payments with subscriptions | About 3 to 6 weeks | Payment methods, proration, taxes, failed-payment retries |
| CRM sync | About 2 to 5 weeks | Field mapping, duplicate handling, one-way or two-way sync |
| ERP integration | About 6 to 12 weeks or more | Legacy API quality, test environments, data cleanup |
Payment gateway: what drives the range
Weeks grow with the number of payment methods, partial refunds, taxes, currencies and subscriptions. Each adds states your handler must track. A candidate who asks about these in the first call is scoping honestly. The phases below show how a mid-sized embedded integration might run.
- Week 1 (illustrative)
Scoping and sandbox
Agree payment model, methods and events. Connect a test account and confirm the data path.
- Weeks 2 to 3
Build and webhook handling
Build the checkout, the signed webhook endpoint, the event table and the worker.
- Week 4
Test the failure cases
Replay duplicates, simulate declines, refunds and a provider outage, and review the alerts.
- Week 5
Go live and reconcile
Switch to live keys, watch the first orders and run the first daily reconciliation.
Testing the failure cases deserves its own week, because that is where cheap quotes cut corners.
CRM sync: field mapping is the work
The API call that creates a contact is easy. The work is deciding what a lead is, which system wins when two values disagree and how to avoid duplicates. Effort follows the number of fields, objects and sync directions. A one-way form-to-CRM flow is quick, while a two-way sync with deal stages takes longer.
ERP: legacy constraints and testing
ERP projects take longest because the other side sets the pace: older APIs, batch-only exports, limited test environments and data that needs cleaning. Budget time for a sandbox you can break, and for a plan that keeps stock, price and order data in step. The post on API and legacy system integrations for enterprise websites goes deeper into this case.
Whatever the system, ask for a fixed-scope discovery phase first. The estimate for the build then rests on what was learned, not on a guess.
Who owns the integration after launch
You should own every account, key and line of code the integration uses, from the first day. Ownership turns a change of builder into a handover instead of a rescue. It is also the simplest test of a partner's confidence in their own work.
Accounts and keys in your name
Create the payment, CRM and ERP accounts yourself, and invite the builder as a user with limited permissions. The builder generates restricted API keys inside your accounts, and you can revoke them whenever you wish. Be wary of arrangements where the builder's merchant account processes your payments, because your history and settlements cannot move with you.
Code, docs and runbooks you receive
At handover you should hold:
- The repository in your own organisation, with full history.
- A README with setup steps and every environment variable named.
- A list of the webhook events handled and what each one does.
- A runbook for common failures, such as a stuck queue or a missed event.
- Tests that cover the failure paths, and a diagram of the data path.
Maintenance when providers change their API
Providers deprecate API versions, rotate certificates and change webhook payloads. Someone must read those notices and test the changes. Decide whether your team, the original builder or a maintenance plan covers it, and write it into the agreement. At werun.dev that is what the Care plan and the monthly Retainer are for, and code and accounts stay in your name either way.
Vet a payment gateway integration developer
You can judge a candidate in one or two calls if you know what to listen for. Use the red flags to screen proposals and the questions to test experience.
Red flags in a proposal
- They ask for your secret API key by email or chat, or want to use their own merchant account.
- Orders are fulfilled on the success page, with no webhook.
- The quote is a single line with no mention of refunds, disputes or retries.
- Card numbers or full request bodies end up in logs.
- Test and live keys share one configuration, or there is no test plan.
- The code lives only in the builder's repository, with no handover terms.
- PCI is 'handled' with no payment model named.
Questions that reveal real experience
Ask these and listen for specifics: named events, named tables, named failure cases. Treat the list as a scorecard, with one point for each concrete answer.
- Who holds the API keys and the merchant account, and where do the keys live?
- Which payment model do you propose, and what does it mean for our PCI questionnaire?
- How do you verify webhook signatures on the raw body?
- How do you handle a duplicate or out-of-order event?
- Which idempotency key do you send on a charge and on a refund?
- What happens when we hit a rate limit, or the provider is down for an hour?
- Who gets the alert at 3 a.m. and how do you detect a missed event?
- What do we receive at handover: repository, docs, runbooks and tests?
A candidate who has done this before answers most of these without hesitation, and often adds a story about a time it went wrong. That story is useful evidence.
Where to start this week
Write a one-page integration brief before you talk to anyone. List the systems involved, the events that move between them (order paid, refund issued, contact created, stock changed) and which system is the source of truth for each. Add the payment methods you need, the countries you sell in and who will own the integration afterwards.
That page lets any candidate give a comparable quote, and it shows you quickly who asks good questions. Take the checklist above to two or three calls and compare the answers.
If you want a second pair of eyes, tell us your goal. werun.dev builds payment, CRM and ERP integrations with code, accounts and keys in your name, and we reply in under 2 hours on business days. Send us the brief and we will tell you which payment model and timeline we would propose.
Frequently asked questions
Who can integrate my website with a payment gateway?
An in-house engineer, a freelancer, an integration agency or a platform connector app can all do it. Pick by the number of systems, the payment model and who will maintain the code afterwards. Whoever you choose should work in accounts you own and hand over code and documentation.
How much does a payment gateway integration cost?
It depends on scope more than on the gateway. A hosted checkout takes roughly 1 to 2 weeks of work, while embedded payments with subscriptions, taxes and refunds often take several weeks more (illustrative ranges). Ask for a quote broken down by payment methods, refunds, taxes and testing.
Is hosted checkout or embedded payment better for PCI compliance?
Hosted checkout keeps card entry entirely on the provider's page, which gives the smallest PCI scope. Embedded fields also send card data straight to the provider, but your page loads the provider's script, so your responsibilities grow slightly. Confirm the questionnaire that applies with your provider.
What is an idempotency key in payment integrations?
An idempotency key is a unique value sent with a request so that retrying it returns the original result instead of repeating the action. For payments, it prevents double charges after a timeout. Build it from a stable identifier such as the order ID.
How long does an ERP or CRM API integration take?
Illustrative ranges are about 2 to 5 weeks for a CRM sync and 6 to 12 weeks or more for an ERP. The time goes into field mapping, legacy API limits, test environments and data cleanup rather than into the API calls themselves.
What should I ask before I hire an API integration developer?
Ask who holds the keys, which payment model they propose, how they verify webhook signatures, how they handle duplicates and rate limits, who receives alerts and what you get at handover. Specific answers show real experience.