Payments
Stripe South Africa setup guide.
Stripe went GA in South Africa in 2024 after years of Atlas-only access, and it is now a realistic option for SA-registered businesses that want a modern card-processing stack. You get ZAR payouts to a local bank account, the usual Stripe SDKs, Checkout, Payment Links and Apple Pay. The rough edges are real though: dispute/chargeback tooling in South Africa is more limited than in the US/EU, recurring billing via Stripe Billing works for some currencies but not fully for ZAR in every scenario, and payout timing is slower than a local gateway. This guide walks through account activation, Apple Pay domain verification, webhook setup and the constraints to budget for.
Prerequisites
- SA-registered Pty Ltd, Sole Proprietor, or Trust with CIPC and FICA docs available
- SA business bank account for ZAR payouts
- Domain with HTTPS for Checkout and Apple Pay verification
- Shopify plan or custom checkout (Shopify does not currently support Stripe as a native SA gateway — so Stripe is for custom stacks, Hydrogen, or non-Shopify SA merchants)
- Webhook endpoint with HTTPS and raw body access for signature verification
Step 1. Activate a South African Stripe account
Sign up at stripe.com/za and complete the activation flow — business type, CIPC registration number, director details, tax number (income tax ref for sole props, company income tax for Pty Ltds), and bank account. Stripe may request extra KYC docs for new accounts or high-risk categories.
Step 2. Set ZAR as the default and understand currency support
ZAR is supported as a settlement currency and as a presentation currency. Stripe will auto-convert if you charge in a non-settlement currency — expect conversion fees. Some products (parts of Stripe Billing subscriptions, certain Connect payouts, some international card methods) have ZAR-specific constraints — verify each against current Stripe docs for ZA.
Step 3. Install Stripe SDK and create a PaymentIntent
For one-off card payments, the modern pattern is PaymentIntent plus Payment Element. Create the PaymentIntent on the server, pass client_secret to the browser, and confirm client-side.
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-06-20',
});
export async function createPaymentIntent(order: {
id: string;
amountRands: number;
email: string;
}) {
return stripe.paymentIntents.create({
amount: Math.round(order.amountRands * 100), // cents
currency: 'zar',
automatic_payment_methods: { enabled: true },
metadata: { orderId: order.id },
receipt_email: order.email,
});
} Step 4. Render the Payment Element on your checkout
Mount Stripe.js with your publishable key and pass the client_secret. The Payment Element renders card input plus any enabled alternative methods, handles 3DS challenges inline, and surfaces localised errors.
<script src="https://js.stripe.com/v3/"></script>
<script type="module">
const stripe = Stripe('pk_live_...');
const elements = stripe.elements({ clientSecret: 'pi_..._secret_...' });
elements.create('payment').mount('#payment-element');
document.querySelector('#pay').addEventListener('click', async () => {
const { error } = await stripe.confirmPayment({
elements,
confirmParams: { return_url: 'https://yourstore.co.za/checkout/return' },
});
if (error) document.querySelector('#err').textContent = error.message;
});
</script>
<div id="payment-element"></div>
<button id="pay">Pay</button>
<div id="err"></div> Step 5. Verify your domain for Apple Pay
In Stripe dashboard → Settings → Payment methods → Apple Pay, register your domain. Stripe gives you a file to host at /.well-known/apple-developer-merchantid-domain-association. Apple Pay in SA works on Safari on supported devices, and coverage is improving — expect only a slice of buyers to see it initially.
Step 6. Set up webhooks and verify signatures
Dashboard → Developers → Webhooks → Add endpoint. Subscribe to payment_intent.succeeded, payment_intent.payment_failed, charge.refunded, and charge.dispute.created at minimum. Verify the Stripe-Signature header using the webhook secret before trusting any payload.
export async function handleStripeWebhook(req: Request) {
const raw = await req.text();
const sig = req.headers.get('stripe-signature') || '';
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
raw,
sig,
process.env.STRIPE_WEBHOOK_SECRET!,
);
} catch {
return new Response('bad signature', { status: 400 });
}
switch (event.type) {
case 'payment_intent.succeeded': {
const pi = event.data.object as Stripe.PaymentIntent;
await markOrderPaid(pi.metadata.orderId!, pi.id);
break;
}
case 'charge.dispute.created':
await flagOrderForDispute(event.data.object as Stripe.Dispute);
break;
}
return new Response('ok');
} Step 7. Test with Stripe test mode and test cards
Use test keys (sk_test_…) and the official test cards — 4242 4242 4242 4242 for success, 4000 0000 0000 0002 for decline, 4000 0025 0000 3155 for 3DS challenge. Run every path including webhook retries (use stripe listen from the Stripe CLI).
Step 8. Go live and monitor first-30-days metrics
Swap to live keys, enable live webhooks, and watch auth rate plus dispute rate daily. SA issuers sometimes decline CNP transactions for new merchants — you may need to support Stripe's Radar rules or engage Stripe support if authorisation rate is below 85%.
SA gotchas
- Card fees for ZA-issued cards are approximately 2.9% + R2.50 (verify current published rate — international cards and currency conversion carry extra fees). Enterprise volume pricing is negotiable.
- Dispute and chargeback tooling in South Africa is more limited than in the US/EU. Evidence submission works but response times and win rates trend lower — factor this into your fraud tolerance.
- Payouts to SA bank accounts are typically T+7 on new accounts, reducing to T+2 as history builds. Slower than Payfast or Yoco.
- Stripe Billing subscription support for ZAR works for most cases but has edge cases around tax handling and certain invoice features — verify your exact scenario against current Stripe ZA docs.
- Shopify does not currently support Stripe as a native payment gateway for SA merchants. Stripe is for Hydrogen/custom/non-Shopify builds here — on Shopify use Payfast, Peach or Yoco.
- Apple Pay in SA requires domain verification plus HTTPS plus Safari plus a supported Apple device. Coverage is real but still smaller than global — do not assume Apple Pay alone is enough for mobile.
Frequently asked questions
Is Stripe POPIA-compliant?
Stripe publishes a POPIA-aligned DPA and is PCI-DSS Level 1. Card data flows through Stripe-hosted elements, which keeps you on PCI SAQ A. You still need to document Stripe as an Operator in your privacy notice.
Does Stripe support recurring billing in ZAR?
Largely yes through Stripe Billing, but there are edge cases — some tax features, certain invoice scenarios and Connect payout patterns differ for ZAR. Verify your specific scenario against current Stripe ZA docs before relying on it for subscriptions.
Is 3DS2 handled automatically?
Yes. The Payment Element runs 3DS2 challenges inline on SA-issued cards per the Visa/Mastercard mandate. You do not implement the challenge UI — Stripe does.
How does Stripe compare to Payfast for a SA Shopify store?
On Shopify specifically, Payfast remains the practical choice because Stripe is not a native Shopify gateway for SA merchants. For custom checkouts or Hydrogen stores, Stripe is the stronger developer experience.
How long does payout take?
Typically T+7 business days for new SA accounts, dropping to T+2 as the account matures. Faster payouts are not currently available for ZAR the way they are for USD.