Recurring Payments
Overview
Recurring payments require two steps:
- Save a payment method — collect card details once and store a
pm_xxxtoken on the customer - Charge the saved method — create and confirm payment intents using the stored token, without the customer present
Save a payment method (Setup Intent)
A setup intent guides the customer through saving their payment method. Create one server-side, pass the client_secret to the widget, and the widget handles card collection.
curl -X POST https://api.elasticpay.co/api/v1/setup_intents \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"customer_id": "cus_0abc123def456"}'const res = await fetch("https://api.elasticpay.co/api/v1/setup_intents", { method: "POST", headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, body: JSON.stringify({ customer_id: "cus_0abc123def456" }),});const { client_secret } = await res.json();import requests
res = requests.post( "https://api.elasticpay.co/api/v1/setup_intents", headers={ "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, json={"customer_id": "cus_0abc123def456"},)client_secret = res.json()["client_secret"]<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => [ 'Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json', ],]);
$res = $client->post('/api/v1/setup_intents', [ 'json' => ['customer_id' => 'cus_0abc123def456'],]);$data = json_decode($res->getBody(), true);$client_secret = $data['client_secret'];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'
res = conn.post('/api/v1/setup_intents') do |req| req.body = { customer_id: 'cus_0abc123def456' }endclient_secret = res.body['client_secret']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");
var res = await http.PostAsJsonAsync("/api/v1/setup_intents", new { customer_id = "cus_0abc123def456" });var si = await res.Content.ReadFromJsonAsync<JsonElement>();var clientSecret = si.GetProperty("client_secret").GetString();After the customer completes the widget flow, the setup_intent.succeeded event fires and the saved pm_xxx is available on the customer record.
Charge a saved payment method
Create a payment intent and confirm it server-side using the saved pm_xxx. No customer interaction required:
# 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": 4900, "currency": "AUD", "customer_id": "cus_0abc123def456"}'
# 2. Confirm with saved 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"}'// 1. Create payment intentconst createRes = await fetch("https://api.elasticpay.co/api/v1/payment_intents", { method: "POST", headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Content-Type": "application/json", }, body: JSON.stringify({ amount: 4900, currency: "AUD", customer_id: "cus_0abc123def456" }),});const { id } = await createRes.json();
// 2. Confirm with saved payment methodconst confirmRes = await fetch( `https://api.elasticpay.co/api/v1/payment_intents/${id}/confirm`, { method: "POST", headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "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": 4900, "currency": "AUD", "customer_id": "cus_0abc123def456"},).json()
# 2. Confirm with saved 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' => 4900, 'currency' => 'AUD', 'customer_id' => 'cus_0abc123def456'],]);$pi = json_decode($createRes->getBody(), true);
// 2. Confirm with saved 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: 4900, currency: 'AUD', customer_id: 'cus_0abc123def456' }endpi_id = create_res.body['id']
# 2. Confirm with saved 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 = 4900, currency = "AUD", customer_id = "cus_0abc123def456"});var pi = await createRes.Content.ReadFromJsonAsync<JsonElement>();var piId = pi.GetProperty("id").GetString();
// 2. Confirm with saved payment methodvar confirmRes = await http.PostAsJsonAsync( $"/api/v1/payment_intents/{piId}/confirm", new { payment_method = "pm_0xyz789abc123def456ghi012jkl" });var result = await confirmRes.Content.ReadFromJsonAsync<JsonElement>();List saved payment methods
Retrieve all saved payment methods for a customer:
curl https://api.elasticpay.co/api/v1/customers/cus_0abc123def456/payment_methods \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"const res = await fetch( "https://api.elasticpay.co/api/v1/customers/cus_0abc123def456/payment_methods", { headers: { "Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } });const { data } = await res.json();import requests
res = requests.get( "https://api.elasticpay.co/api/v1/customers/cus_0abc123def456/payment_methods", headers={"Authorization": "Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},)data = res.json()<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => [ 'Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type' => 'application/json', ],]);
$res = $client->get('/api/v1/customers/cus_0abc123def456/payment_methods');$data = json_decode($res->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'
res = conn.get('/api/v1/customers/cus_0abc123def456/payment_methods')data = res.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");
var data = await http.GetFromJsonAsync<JsonElement>( "/api/v1/customers/cus_0abc123def456/payment_methods");Managing payment plans
For scheduled recurring payments (weekly, monthly, etc.), use the portal’s Payment Plans feature instead of scheduling intents manually. The portal handles scheduling, failure recovery, and retry logic. See Understanding Plans for details.