To integrate Replit with Worldpay, obtain your Worldpay Access API credentials from the Worldpay merchant portal, store them in Replit Secrets, and build a server-side Node.js or Python backend to handle payment authorization, 3DS2 authentication, and settlement flows. Worldpay's enterprise-grade global processing requires careful server-side implementation. Deploy on Replit Reserved VM for always-on payment infrastructure.
Why Integrate Worldpay with Replit?
Worldpay processes over 40 billion transactions annually and is the preferred payment processor for many large enterprises, particularly in the UK, Europe, and global multi-currency environments. For merchants already using Worldpay as their acquirer, integrating Worldpay into a custom application built on Replit enables direct control over the payment flow — custom checkout experiences, server-to-server payment processing for recurring billing, and integration of Worldpay's advanced fraud prevention and 3D Secure 2 authentication.
The Worldpay Access API is a modern REST API that covers the complete payment lifecycle from authorization through settlement. Unlike consumer payment tools, Worldpay is designed for enterprises processing high volumes of transactions across multiple currencies and payment methods. The API supports Visa, Mastercard, American Express, and many regional payment methods. 3DS2 is mandatory for card-not-present transactions under PSD2 in Europe, and the Worldpay Access API provides the full 3DS2 authentication flow including the Challenge and Frictionless paths.
Building on Replit gives you a cloud server environment to implement the Worldpay backend without managing your own infrastructure. However, because you're handling payment flows, PCI DSS compliance is critical — never let raw card numbers touch your Replit server. Always use Worldpay's hosted payment page or the Access Checkout JavaScript SDK to collect card details client-side, passing only payment session tokens to your server. For production workloads, deploy on Replit Reserved VM to ensure payment endpoints are always available without cold start delays.
Integration method
Worldpay integrates with Replit through the Worldpay Access API, a REST API that replaces the older XML-based Worldpay APIs. You store your API username and password credentials in Replit Secrets and make HTTPS requests from a server-side Node.js or Python backend. The integration covers the full payment lifecycle: authorization, 3DS2 authentication challenges, capture, refund, and inquiry. All card data is collected via Worldpay's hosted payment page or the Access Checkout SDK to maintain PCI DSS compliance.
Prerequisites
- A Worldpay merchant account with Access API credentials (username and password from your Worldpay merchant portal or onboarding team)
- Your Worldpay merchant entity reference and the API environment URLs (sandbox and production) from your Worldpay documentation pack
- A Replit account with a Node.js or Python Repl created
- Node.js with axios and express, or Python with requests and flask — install via Shell
- Understanding of PCI DSS compliance requirements: card data must never be processed or stored on your server; use Worldpay's hosted fields or Access Checkout SDK for card collection
Step-by-step guide
Obtain Worldpay Access API Credentials and Store in Replit Secrets
Obtain Worldpay Access API Credentials and Store in Replit Secrets
The Worldpay Access API uses HTTP Basic Authentication — every request includes a Base64-encoded username:password pair in the Authorization header. Your API credentials (a service username and password) are provided by your Worldpay onboarding team or are available in the Worldpay Business Gateway under your account settings. There are two sets of credentials: sandbox credentials for testing (pointing to Worldpay's test environment) and production credentials for live processing. Open your Replit project and click the lock icon (🔒) in the left sidebar to open Secrets. Add the following secrets: WORLDPAY_SERVICE_KEY (your API username), WORLDPAY_CLIENT_KEY (your API password), WORLDPAY_MERCHANT_CODE (your merchant entity reference), and WORLDPAY_ENV with the value 'sandbox' or 'production'. You'll also need the entity reference for your merchant profile, which is included in Worldpay's API request URLs. Never hardcode these values in your source code — Replit's Secret Scanner monitors your files and will flag exposed credentials. For sandbox testing, Worldpay provides test card numbers (such as 4111111111111111 for a successful Visa transaction) that you can use without real card data.
1// Node.js: credential setup and authentication helper2const WORLDPAY_SERVICE_KEY = process.env.WORLDPAY_SERVICE_KEY;3const WORLDPAY_CLIENT_KEY = process.env.WORLDPAY_CLIENT_KEY;4const WORLDPAY_MERCHANT_CODE = process.env.WORLDPAY_MERCHANT_CODE;5const WORLDPAY_ENV = process.env.WORLDPAY_ENV || 'sandbox';67const WORLDPAY_BASE_URL = WORLDPAY_ENV === 'production'8 ? 'https://access.worldpay.com'9 : 'https://try.access.worldpay.com';1011// HTTP Basic Auth header12const basicAuth = Buffer.from(`${WORLDPAY_SERVICE_KEY}:${WORLDPAY_CLIENT_KEY}`).toString('base64');1314const worldpayHeaders = {15 'Authorization': `Basic ${basicAuth}`,16 'Content-Type': 'application/json',17 'Accept': 'application/json'18};1920if (!WORLDPAY_SERVICE_KEY || !WORLDPAY_CLIENT_KEY || !WORLDPAY_MERCHANT_CODE) {21 console.error('ERROR: Worldpay credentials not set in Replit Secrets. Add WORLDPAY_SERVICE_KEY, WORLDPAY_CLIENT_KEY, and WORLDPAY_MERCHANT_CODE.');22 process.exit(1);23}Pro tip: Always start development and testing on Worldpay's sandbox environment (try.access.worldpay.com) before switching to production credentials. The WORLDPAY_ENV secret makes it easy to switch environments without changing code.
Expected result: Your Worldpay credentials are stored as Replit Secrets, accessible as environment variables, and your server logs an error if any required credential is missing at startup.
Build the Payment Authorization Endpoint
Build the Payment Authorization Endpoint
The core of the Worldpay Access API integration is the payment authorization endpoint. The payment flow works as follows: the customer's browser collects card data using Worldpay's Access Checkout JavaScript SDK (which returns a session reference, not raw card data), your server sends an authorization request to Worldpay's /payments endpoint with the session reference, amount, currency, and order details, and Worldpay returns either an approved authorization, a decline, or a 3DS2 challenge required response. The authorization request body includes the transaction value and currency, the merchant entity reference, the payment instrument (session reference from the client), and risk/fraud prevention data. If Worldpay returns a 3DS2 challenge required response, you must redirect the customer to the issuing bank's authentication page. Install axios by running npm install express axios in the Replit Shell.
1const express = require('express');2const axios = require('axios');3const crypto = require('crypto');45const app = express();6app.use(express.json());78// Worldpay config (from worldpay-config.js or inline)9const WORLDPAY_SERVICE_KEY = process.env.WORLDPAY_SERVICE_KEY;10const WORLDPAY_CLIENT_KEY = process.env.WORLDPAY_CLIENT_KEY;11const WORLDPAY_MERCHANT_CODE = process.env.WORLDPAY_MERCHANT_CODE;12const WORLDPAY_BASE_URL = process.env.WORLDPAY_ENV === 'production'13 ? 'https://access.worldpay.com'14 : 'https://try.access.worldpay.com';1516const basicAuth = Buffer.from(`${WORLDPAY_SERVICE_KEY}:${WORLDPAY_CLIENT_KEY}`).toString('base64');17const worldpayHeaders = {18 'Authorization': `Basic ${basicAuth}`,19 'Content-Type': 'application/json',20 'Accept': 'application/json'21};2223// Authorize a payment24app.post('/api/payments/authorize', async (req, res) => {25 const { sessionHref, amount, currencyCode, orderDescription, merchantReference } = req.body;2627 if (!sessionHref || !amount || !currencyCode) {28 return res.status(400).json({ error: 'sessionHref, amount, and currencyCode are required' });29 }3031 const reference = merchantReference || `ORDER-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;3233 const authorizationPayload = {34 transactionReference: reference,35 merchant: { entity: WORLDPAY_MERCHANT_CODE },36 instruction: {37 narrative: { line1: orderDescription || 'Order payment' },38 value: {39 currency: currencyCode,40 amount: Math.round(amount * 100) // Convert to minor units (cents/pence)41 },42 paymentInstrument: {43 type: 'card/checkout',44 sessionHref: sessionHref45 }46 }47 };4849 try {50 const response = await axios.post(51 `${WORLDPAY_BASE_URL}/payments/authorizations`,52 authorizationPayload,53 { headers: worldpayHeaders, timeout: 30000 }54 );5556 const result = response.data;57 const outcome = result.outcome;5859 if (outcome === 'authorized') {60 res.json({61 success: true,62 transactionReference: reference,63 authorizationCode: result.authorizationCode,64 outcome: outcome65 });66 } else if (outcome === 'challenged') {67 // 3DS2 challenge required — return challenge details to frontend68 res.json({69 success: false,70 requires3DS: true,71 transactionReference: reference,72 challenge: result._links?.['payments:challenge'] || null,73 outcome: outcome74 });75 } else {76 res.json({77 success: false,78 transactionReference: reference,79 outcome: outcome,80 description: result.description81 });82 }83 } catch (error) {84 const apiError = error.response?.data;85 console.error('Worldpay authorization error:', apiError || error.message);86 res.status(error.response?.status || 500).json({87 error: 'Payment authorization failed',88 details: apiError?.message || error.message89 });90 }91});9293app.listen(3000, '0.0.0.0', () => console.log('Worldpay payment server running on port 3000'));Pro tip: The Worldpay Access API expects monetary amounts in minor currency units — pence for GBP, cents for USD and EUR. Always multiply your decimal amount by 100 and round to an integer before sending it to the API (e.g., £12.99 becomes 1299).
Expected result: POST /api/payments/authorize with valid session data returns either an authorization confirmation, a 3DS2 challenge object, or a decline response.
Handle 3DS2 Authentication
Handle 3DS2 Authentication
3D Secure 2 (3DS2) is mandatory for most card-not-present transactions in Europe under PSD2 Strong Customer Authentication requirements. The Worldpay Access API supports both the frictionless path (where the card issuer approves without user interaction based on risk scoring) and the challenge path (where the customer is presented with an authentication challenge from their bank, typically a one-time code). When your authorization endpoint receives an 'outcome: challenged' response, it means the card issuer requires the customer to complete an authentication step. The response includes a challenge URL and a three-domain secure object. Your frontend must redirect the customer to this challenge URL (or display it in an iframe) so the issuer can authenticate them. After authentication, the issuer redirects back to your specified return URL with a session reference. You then complete the payment by submitting a second authorization request with the completed 3DS authentication data. This step confirms the authentication was successful and finalizes the payment.
1// Handle 3DS2 completion after customer authenticates with their bank2app.post('/api/payments/3ds-complete', async (req, res) => {3 const { transactionReference, authenticationValue, eci, dsTransactionId } = req.body;45 if (!transactionReference) {6 return res.status(400).json({ error: 'transactionReference is required' });7 }89 const completionPayload = {10 transactionReference: transactionReference,11 merchant: { entity: WORLDPAY_MERCHANT_CODE },12 authentication: {13 version: '2.1.0',14 authenticationValue: authenticationValue,15 eci: eci,16 dsTransactionId: dsTransactionId17 }18 };1920 try {21 const response = await axios.post(22 `${WORLDPAY_BASE_URL}/payments/authorizations/threeDS/complete`,23 completionPayload,24 { headers: worldpayHeaders, timeout: 30000 }25 );2627 const result = response.data;28 res.json({29 success: result.outcome === 'authorized',30 transactionReference: transactionReference,31 outcome: result.outcome,32 authorizationCode: result.authorizationCode || null33 });34 } catch (error) {35 console.error('3DS completion error:', error.response?.data || error.message);36 res.status(error.response?.status || 500).json({37 error: '3DS completion failed',38 details: error.response?.data?.message || error.message39 });40 }41});4243// Inquiry endpoint to check transaction status44app.get('/api/payments/:reference', async (req, res) => {45 try {46 const response = await axios.get(47 `${WORLDPAY_BASE_URL}/payments/authorizations/${req.params.reference}`,48 { headers: worldpayHeaders, timeout: 15000 }49 );50 res.json(response.data);51 } catch (error) {52 console.error('Inquiry error:', error.response?.data || error.message);53 res.status(error.response?.status || 500).json({ error: 'Transaction inquiry failed' });54 }55});Pro tip: In the Worldpay sandbox environment, you can simulate frictionless 3DS2 by using specific test card numbers provided in the Worldpay sandbox documentation. This lets you test both the frictionless path and the challenge path without a real card.
Expected result: POST /api/payments/3ds-complete successfully completes a 3DS2 authenticated transaction, returning an authorized status and authorization code.
Implement Capture and Refund
Implement Capture and Refund
Worldpay's authorization flow by default performs an immediate authorization and capture (settlement). However, for physical goods or scenarios where you want to authorize first and capture only when goods ship, you can use a two-stage authorization/capture flow. To capture a previously authorized but not yet captured transaction, POST to the captures endpoint with the transaction reference. To issue a full or partial refund on a settled transaction, POST to the refunds endpoint. Both operations are idempotent — re-submitting the same request with the same reference won't double-charge or double-refund. Build these endpoints in Python to show the Flask alternative as well, so teams using Python can implement the full payment lifecycle.
1import os2import base643import requests4from flask import Flask, jsonify, request56app = Flask(__name__)78SERVICE_KEY = os.environ['WORLDPAY_SERVICE_KEY']9CLIENT_KEY = os.environ['WORLDPAY_CLIENT_KEY']10MERCHANT_CODE = os.environ['WORLDPAY_MERCHANT_CODE']11ENV = os.environ.get('WORLDPAY_ENV', 'sandbox')1213BASE_URL = 'https://access.worldpay.com' if ENV == 'production' else 'https://try.access.worldpay.com'14AUTH_HEADER = base64.b64encode(f'{SERVICE_KEY}:{CLIENT_KEY}'.encode()).decode()15HEADERS = {16 'Authorization': f'Basic {AUTH_HEADER}',17 'Content-Type': 'application/json',18 'Accept': 'application/json'19}2021@app.route('/api/payments/<reference>/capture', methods=['POST'])22def capture_payment(reference):23 data = request.json or {}24 capture_value = data.get('amount')2526 payload = {27 'transactionReference': reference,28 'merchant': {'entity': MERCHANT_CODE}29 }30 if capture_value:31 payload['value'] = {32 'currency': data.get('currency', 'GBP'),33 'amount': int(float(capture_value) * 100)34 }3536 try:37 resp = requests.post(38 f'{BASE_URL}/payments/settlements/captures',39 json=payload, headers=HEADERS, timeout=3040 )41 resp.raise_for_status()42 return jsonify({'success': True, 'reference': reference, 'result': resp.json()})43 except requests.RequestException as e:44 error_data = e.response.json() if e.response else {'message': str(e)}45 return jsonify({'error': 'Capture failed', 'details': error_data}), 5004647@app.route('/api/payments/<reference>/refund', methods=['POST'])48def refund_payment(reference):49 data = request.json or {}50 if not data.get('amount') or not data.get('currency'):51 return jsonify({'error': 'amount and currency are required'}), 4005253 payload = {54 'transactionReference': reference,55 'merchant': {'entity': MERCHANT_CODE},56 'value': {57 'currency': data['currency'],58 'amount': int(float(data['amount']) * 100)59 }60 }6162 try:63 resp = requests.post(64 f'{BASE_URL}/payments/settlements/refunds',65 json=payload, headers=HEADERS, timeout=3066 )67 resp.raise_for_status()68 return jsonify({'success': True, 'refund_reference': reference, 'result': resp.json()})69 except requests.RequestException as e:70 error_data = e.response.json() if e.response else {'message': str(e)}71 return jsonify({'error': 'Refund failed', 'details': error_data}), 5007273if __name__ == '__main__':74 app.run(host='0.0.0.0', port=3000)Pro tip: Refund amounts are also specified in minor currency units (pence/cents). If a customer paid £49.99, a full refund requires an amount of 4999 in the API request. Partial refunds must be less than or equal to the original transaction amount.
Expected result: POST /api/payments/{reference}/refund successfully issues a refund and returns the refund reference from Worldpay.
Configure Replit for Production Payment Processing
Configure Replit for Production Payment Processing
For a production payment backend, Replit Reserved VM is the required deployment type — never use Autoscale for payment infrastructure, as Autoscale can scale to zero and experience cold starts during periods of low traffic, which is unacceptable for checkout flows. Reserved VM provides a persistent server with no cold starts, guaranteed uptime, and fixed resource allocation. Configure your .replit file to specify the deployment target and port binding. Before going live with production Worldpay credentials, complete the following checklist: update WORLDPAY_ENV in Replit Secrets to 'production', replace sandbox credentials with production credentials (update all three secrets), ensure your server enforces HTTPS (Replit deployment provides SSL automatically), add rate limiting to your payment endpoints to prevent abuse, implement request idempotency using merchant transaction references to prevent duplicate charges, and add comprehensive logging for all payment attempts and outcomes for audit purposes. Worldpay requires that you log transaction references for reconciliation. Also confirm that your PCI DSS implementation is correct — if your Replit server handles only session tokens and never raw card numbers, you're in scope for SAQ A-EP rather than full SAQ D.
1# .replit configuration for production payment server2# Update this in your .replit file34# [deployment]5# run = ["node", "server.js"]6# deploymentTarget = "cloudrun"7#8# [[ports]]9# internalPort = 300010# externalPort = 801112# Add rate limiting to Express (npm install express-rate-limit)13const rateLimit = require('express-rate-limit');1415const paymentLimiter = rateLimit({16 windowMs: 60 * 1000, // 1 minute17 max: 10, // max 10 payment attempts per minute per IP18 message: { error: 'Too many payment attempts. Please wait before trying again.' },19 standardHeaders: true20});2122app.post('/api/payments/authorize', paymentLimiter, async (req, res) => {23 // ... payment authorization code24});2526# Idempotency: generate deterministic transaction references27function generateMerchantReference(orderId, customerId) {28 // Include order and customer IDs to make reference deterministic29 // Re-submitting with the same reference won't duplicate the charge30 return `ORD-${orderId}-CUST-${customerId}-${Date.now()}`;31}Pro tip: Enable Replit's Reserved VM deployment for payment endpoints — never rely on Autoscale for checkout infrastructure since scale-to-zero behavior causes unacceptable latency spikes at the most critical moment of the customer journey.
Expected result: Your Worldpay payment server is deployed on Replit Reserved VM, accessible via a stable HTTPS URL, with rate limiting and idempotency controls in place.
Common use cases
Server-to-Server Payment Authorization
Build a Replit backend that accepts tokenized card data from Worldpay's Access Checkout SDK (running in the browser), then sends a payment authorization request to the Worldpay Access API server-side. The server handles the full authorization, 3DS2 challenge routing, and returns the authorization response to the frontend without raw card numbers ever leaving the browser.
Build a payment authorization endpoint that accepts a Worldpay session token and order details (amount, currency, merchant reference), sends an authorization request to the Worldpay Access API, handles the 3DS2 response (either frictionless approval or challenge redirect), and returns the authorization result with the transaction reference.
Copy this prompt to try it in Replit
Recurring Billing with Payment Tokens
After an initial card-present or card-not-present transaction, Worldpay can return a token representing the card. Use this token for recurring billing charges without requiring the customer to re-enter card details. Your Replit backend stores the token reference and uses it to initiate subsequent charges via the Worldpay Access API's repeat payment flow.
Build a recurring billing system that stores customer payment tokens returned from initial Worldpay transactions, and creates a scheduled endpoint that submits repeat authorization requests using stored tokens for monthly subscription charges, logging each transaction result to a database.
Copy this prompt to try it in Replit
Payment Refund and Inquiry System
Build an admin API on Replit that lets your operations team look up transaction status and issue full or partial refunds through the Worldpay Access API. The server validates refund requests, checks the original transaction status, and submits the refund to Worldpay, returning the refund reference number and status.
Build an admin refund endpoint that accepts a Worldpay transaction reference and a refund amount, validates that the refund amount does not exceed the original transaction amount, submits the refund to the Worldpay Access API, and returns the refund reference and status.
Copy this prompt to try it in Replit
Troubleshooting
API returns 401 Unauthorized on every request
Cause: The WORLDPAY_SERVICE_KEY and WORLDPAY_CLIENT_KEY credentials are incorrect, not set in Replit Secrets, or the Base64 encoding of the username:password pair has an error.
Solution: Open the Replit Secrets panel (lock icon 🔒) and verify both WORLDPAY_SERVICE_KEY and WORLDPAY_CLIENT_KEY are set correctly with no extra whitespace. Verify the Base64 encoding is correct: Buffer.from('username:password').toString('base64') in Node.js or base64.b64encode(b'username:password').decode() in Python. Contact Worldpay support to confirm your API credentials are active for the environment you're using.
1// Test your credentials directly2const testAuth = async () => {3 const auth = Buffer.from(`${process.env.WORLDPAY_SERVICE_KEY}:${process.env.WORLDPAY_CLIENT_KEY}`).toString('base64');4 console.log('Auth header:', `Basic ${auth.substring(0, 20)}...`);5};6testAuth();Payment authorization returns 'outcome: refused' with error code 5 (Do Not Honour)
Cause: In the sandbox environment, certain test card numbers always return specific decline codes. Code 5 (Do Not Honour) is the most common generic decline. In production, this means the issuing bank declined the transaction.
Solution: In sandbox testing, use Worldpay's published test card numbers for specific outcomes. For a successful authorization, use 4111111111111111 (Visa) or 5101180000000007 (Mastercard). Check the Worldpay sandbox documentation for test cards that trigger specific decline codes for testing your error handling logic.
3DS2 challenge response is not received or challenge redirect fails
Cause: The 3DS2 challenge flow requires the frontend to redirect the customer to the issuer's authentication URL, and then redirect back to a return URL you specify. If the return URL isn't accessible or HTTPS, or if it's a localhost URL, the redirect will fail.
Solution: Ensure your 3DS2 return URL is a publicly accessible HTTPS URL pointing to your Replit deployment (not the development preview). Test the full 3DS2 flow using Worldpay's sandbox 3DS2 test cards, which trigger the challenge path. The return URL must match what's configured in your Worldpay merchant profile's allowed redirect URLs.
Server returns 500 errors and logs show 'ECONNRESET' or 'socket hang up'
Cause: Worldpay's API can be slow on some requests (up to 10-20 seconds for 3DS2 flows), and Replit's connection may time out before the API responds. The default axios timeout of 0 (no timeout) can also leave requests hanging indefinitely.
Solution: Increase your axios timeout to at least 30 seconds for payment authorization requests and 60 seconds for 3DS2 completion calls. Implement retry logic with exponential backoff for network errors (but NOT for payment authorizations — retry a payment only if you're certain it hasn't been processed to avoid double-charging).
1const response = await axios.post(url, payload, {2 headers: worldpayHeaders,3 timeout: 30000, // 30 seconds for authorization4 validateStatus: (status) => status < 500 // Don't throw on 4xx responses5});Best practices
- Store all Worldpay credentials (service key, client key, merchant code) in Replit Secrets — never hardcode them in source files or commit them to Git
- Use Replit Reserved VM for production payment endpoints — Autoscale's scale-to-zero behavior causes cold starts that are unacceptable in checkout flows
- Never allow raw card numbers to reach your Replit server — use Worldpay's Access Checkout SDK to collect card data in the browser and pass only session tokens to your backend
- Always use minor currency units (pence/cents) in API requests — convert £12.99 to 1299 before sending, never send decimal amounts
- Implement rate limiting on payment endpoints to prevent fraud and accidental double-submissions — use express-rate-limit or Flask-Limiter to cap attempts per IP
- Generate deterministic merchant transaction references (including order ID and timestamp) so that Worldpay can detect and reject duplicate submissions of the same order
- Log every payment attempt with its transaction reference, outcome, and timestamp to a durable store — Worldpay requires transaction records for reconciliation and disputes
- Start all development and testing on Worldpay's sandbox environment (try.access.worldpay.com) — only switch to production credentials (access.worldpay.com) after thorough sandbox testing
Alternatives
Braintree is a simpler integration path for mid-market merchants needing PayPal and Venmo support alongside card payments, with a less complex API than Worldpay.
Sage Pay (now Opayo) is more accessible for UK SMBs with simpler onboarding and integration than enterprise-focused Worldpay.
Stripe offers a far simpler developer experience and broader documentation community than Worldpay, making it the better choice unless your business is already contracted with Worldpay.
Payoneer is the right choice for B2B global payouts and freelancer payments rather than customer-facing checkout processing.
Frequently asked questions
How do I connect Replit to Worldpay?
Store your Worldpay API service key, client key, and merchant code in Replit Secrets (lock icon 🔒 in the sidebar). In your server code, create a Basic Auth header by Base64-encoding 'servicekey:clientkey' and include it as 'Authorization: Basic ...' on all requests to the Worldpay Access API at https://access.worldpay.com (production) or https://try.access.worldpay.com (sandbox).
Does Replit work with Worldpay in sandbox mode?
Yes — Worldpay provides a full sandbox environment at try.access.worldpay.com with test card numbers for simulating different authorization outcomes. Set WORLDPAY_ENV to 'sandbox' in Replit Secrets and use the sandbox credentials from your Worldpay onboarding documentation to test without processing real payments.
Is it safe to process Worldpay payments on Replit?
Yes, if you follow PCI DSS requirements. The critical rule is that raw card numbers must never reach your Replit server — use Worldpay's Access Checkout JavaScript SDK in the browser to collect card data, which returns a session token that your server uses instead of the real card number. Replit's environment itself is secure, but the architecture of your integration must keep card data out of your server code.
How do I handle 3DS2 authentication with Worldpay on Replit?
When Worldpay returns an 'outcome: challenged' response from an authorization request, extract the challenge URL from the response and redirect the customer to it. After the customer completes authentication with their bank, they're redirected back to your configured return URL with authentication data. Pass this data to your Replit server, which then calls the Worldpay 3DS completion endpoint to finalize the payment.
Should I use Replit Autoscale or Reserved VM for Worldpay payment processing?
Always use Reserved VM for production payment endpoints. Autoscale deployments can scale to zero and experience cold start delays of several seconds, which is unacceptable in a payment checkout flow where customers expect immediate responses. Reserved VM provides a constantly running server with no cold starts. The extra monthly cost is worth it for reliable payment processing.
What currency format does the Worldpay Access API expect?
The Worldpay Access API expects monetary amounts in minor currency units — pence for GBP, cents for USD and EUR. You must convert decimal amounts to integers before sending: £29.99 becomes 2999, $100.00 becomes 10000. Always use Math.round(amount * 100) to handle floating-point rounding, never just multiply without rounding.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation