Direct API Integration
When to use direct integration
Direct API integration gives you full control over the payment UI — useful for native mobile apps or highly custom checkout flows. If you handle raw card numbers, your integration becomes in-scope for PCI DSS. See Security & PCI Compliance for details.
For most web integrations, the widget handles card data and keeps you out of PCI scope.
Create and confirm in sequence
Use a tokenised pm_xxx ID to create and immediately confirm a payment intent server-side:
# 1. Create payment intentcurl -X POST https://api.elasticpay.co/api/v1/payment_intents \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"amount": 3500, "currency": "AUD"}'
# 2. Confirm with tokenised payment methodcurl -X POST https://api.elasticpay.co/api/v1/payment_intents/pi_0abc123/confirm \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"payment_method": "pm_0xyz789abc123def456ghi012jkl"}'const BASE = "https://api.elasticpay.co";const AUTH = "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// 1. Create payment intentconst createRes = await fetch(`${BASE}/api/v1/payment_intents`, { method: "POST", headers: { "Authorization": AUTH, "Content-Type": "application/json" }, body: JSON.stringify({ amount: 3500, currency: "AUD" }),});const { id } = await createRes.json();
// 2. Confirm with tokenised payment methodconst confirmRes = await fetch( `${BASE}/api/v1/payment_intents/${id}/confirm`, { method: "POST", headers: { "Authorization": AUTH, "Content-Type": "application/json" }, body: JSON.stringify({ payment_method: "pm_0xyz789abc123def456ghi012jkl" }), });const result = await confirmRes.json();import requests
BASE = "https://api.elasticpay.co"HEADERS = { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json",}
# 1. Create payment intentpi = requests.post( f"{BASE}/api/v1/payment_intents", headers=HEADERS, json={"amount": 3500, "currency": "AUD"},).json()
# 2. Confirm with tokenised payment methodresult = requests.post( f"{BASE}/api/v1/payment_intents/{pi['id']}/confirm", headers=HEADERS, json={"payment_method": "pm_0xyz789abc123def456ghi012jkl"},).json()<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => [ 'Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json', ],]);
// 1. Create payment intent$createRes = $client->post('/api/v1/payment_intents', [ 'json' => ['amount' => 3500, 'currency' => 'AUD'],]);$pi = json_decode($createRes->getBody(), true);
// 2. Confirm with tokenised payment method$confirmRes = $client->post('/api/v1/payment_intents/' . $pi['id'] . '/confirm', [ 'json' => ['payment_method' => 'pm_0xyz789abc123def456ghi012jkl'],]);$result = json_decode($confirmRes->getBody(), true);require 'faraday'require 'json'
conn = Faraday.new('https://api.elasticpay.co') do |f| f.request :json f.response :jsonendconn.headers['Authorization'] = 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'conn.headers['Content-Type'] = 'application/json'
# 1. Create payment intentcreate_res = conn.post('/api/v1/payment_intents') do |req| req.body = { amount: 3500, currency: 'AUD' }endpi_id = create_res.body['id']
# 2. Confirm with tokenised payment methodresult = conn.post("/api/v1/payment_intents/#{pi_id}/confirm") do |req| req.body = { payment_method: 'pm_0xyz789abc123def456ghi012jkl' }endresult = result.bodyusing System.Net.Http;using System.Net.Http.Headers;using System.Net.Http.Json;
using var http = new HttpClient { BaseAddress = new Uri("https://api.elasticpay.co") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
// 1. Create payment intentvar createRes = await http.PostAsJsonAsync("/api/v1/payment_intents", new { amount = 3500, currency = "AUD" });var pi = await createRes.Content.ReadFromJsonAsync<JsonElement>();var piId = pi.GetProperty("id").GetString();
// 2. Confirm with tokenised payment methodvar confirmRes = await http.PostAsJsonAsync( $"/api/v1/payment_intents/{piId}/confirm", new { payment_method = "pm_0xyz789abc123def456ghi012jkl" });var result = await confirmRes.Content.ReadFromJsonAsync<JsonElement>();Idempotency
Add an Idempotency-Key header to safely retry requests without risk of double-charging:
curl -X POST https://api.elasticpay.co/api/v1/payment_intents \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order_9a8b7c6d-unique-key-here" \ -d '{"amount": 3500, "currency": "AUD"}'async function createPaymentWithRetry(amount, idempotencyKey) { const response = await fetch( "https://api.elasticpay.co/api/v1/payment_intents", { method: "POST", headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", "Idempotency-Key": idempotencyKey, }, body: JSON.stringify({ amount, currency: "AUD" }), } ); if (!response.ok) { const error = await response.json(); throw new Error(error.error?.message ?? "Request failed"); } return response.json();}import requestsimport uuid
def create_payment_with_retry(amount, idempotency_key): res = requests.post( "https://api.elasticpay.co/api/v1/payment_intents", headers={ "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", "Idempotency-Key": idempotency_key, }, json={"amount": amount, "currency": "AUD"}, ) res.raise_for_status() return res.json()
# Generate once; reuse on retrykey = str(uuid.uuid4())payment = create_payment_with_retry(3500, key)<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => [ 'Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json', ],]);
function createPaymentWithRetry($client, int $amount, string $idempotencyKey): array { $res = $client->post('/api/v1/payment_intents', [ 'headers' => ['Idempotency-Key' => $idempotencyKey], 'json' => ['amount' => $amount, 'currency' => 'AUD'], ]); return json_decode($res->getBody(), true);}
$key = uniqid('order_', true);$payment = createPaymentWithRetry($client, 3500, $key);require 'faraday'require 'json'
conn = Faraday.new('https://api.elasticpay.co') do |f| f.request :json f.response :jsonendconn.headers['Authorization'] = 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'conn.headers['Content-Type'] = 'application/json'
def create_payment_with_retry(conn, amount, idempotency_key) res = conn.post('/api/v1/payment_intents') do |req| req.headers['Idempotency-Key'] = idempotency_key req.body = { amount: amount, currency: 'AUD' } end res.bodyend
key = SecureRandom.uuidpayment = create_payment_with_retry(conn, 3500, key)using System.Net.Http;using System.Net.Http.Headers;using System.Net.Http.Json;
using var http = new HttpClient { BaseAddress = new Uri("https://api.elasticpay.co") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
async Task<JsonElement> CreatePaymentWithRetry(int amount, string idempotencyKey){ var req = new HttpRequestMessage(HttpMethod.Post, "/api/v1/payment_intents") { Content = JsonContent.Create(new { amount, currency = "AUD" }), }; req.Headers.Add("Idempotency-Key", idempotencyKey); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode(); return await res.Content.ReadFromJsonAsync<JsonElement>();}
var key = Guid.NewGuid().ToString();var payment = await CreatePaymentWithRetry(3500, key);- The same key returns the same response within the TTL window
- Default TTL: 900 seconds; maximum: 1800 seconds
- Keys are scoped per biller account
- Use a UUID or a unique order/transaction identifier as the key