curl --request POST \
--url https://billing.funnelfox.com/{org_id}/v1/checkout/one_click \
--header 'Content-Type: application/json' \
--data '
{
"external_id": "<string>",
"pp_ident": "<string>",
"client_metadata": {},
"initiation_type": null,
"cit_card_networks": null,
"idempotency_key": null
}
'import requests
url = "https://billing.funnelfox.com/{org_id}/v1/checkout/one_click"
payload = {
"external_id": "<string>",
"pp_ident": "<string>",
"client_metadata": {},
"initiation_type": None,
"cit_card_networks": None,
"idempotency_key": None
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
external_id: '<string>',
pp_ident: '<string>',
client_metadata: {},
initiation_type: null,
cit_card_networks: null,
idempotency_key: null
})
};
fetch('https://billing.funnelfox.com/{org_id}/v1/checkout/one_click', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://billing.funnelfox.com/{org_id}/v1/checkout/one_click",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'external_id' => '<string>',
'pp_ident' => '<string>',
'client_metadata' => [
],
'initiation_type' => null,
'cit_card_networks' => null,
'idempotency_key' => null
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://billing.funnelfox.com/{org_id}/v1/checkout/one_click"
payload := strings.NewReader("{\n \"external_id\": \"<string>\",\n \"pp_ident\": \"<string>\",\n \"client_metadata\": {},\n \"initiation_type\": null,\n \"cit_card_networks\": null,\n \"idempotency_key\": null\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://billing.funnelfox.com/{org_id}/v1/checkout/one_click")
.header("Content-Type", "application/json")
.body("{\n \"external_id\": \"<string>\",\n \"pp_ident\": \"<string>\",\n \"client_metadata\": {},\n \"initiation_type\": null,\n \"cit_card_networks\": null,\n \"idempotency_key\": null\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.funnelfox.com/{org_id}/v1/checkout/one_click")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"external_id\": \"<string>\",\n \"pp_ident\": \"<string>\",\n \"client_metadata\": {},\n \"initiation_type\": null,\n \"cit_card_networks\": null,\n \"idempotency_key\": null\n}"
response = http.request(request)
puts response.read_body{
"action_required_token": "<string>",
"checkout_status": "processing",
"failed_message_for_user": "<string>",
"order_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"decline_code": null,
"decline_code_source": "fakeintegration",
"idempotent_replay": false
}Perform a one-click purchase
Initiate a one-click purchase for a given Price Point using the user’s saved payment method.
The payment goes as merchant-initiated (MIT) by default. It goes as customer-initiated (CIT) when one-click settings or initiation_type ask for it and the saved method is a card from a Primer integration. Apple Pay, Google Pay, and PayPal payments, and payments made from the FunnelFox Payments dashboard, always go as MIT.
3DS: A CIT payment can need 3DS. A non-empty action_required_token means the payment is pending until the customer completes the challenge. Complete it with POST /checkout/resume_payment. Until then, another one-click request for the same customer returns an error, unless it reuses the same idempotency_key.
Idempotency: Send one idempotency_key per purchase attempt and reuse it on every retry. Keys are unique per customer and shared with POST /payment/one_off_charge.
- Same key, same Price Point: Charges nothing and returns the original order with
idempotent_replay: true. An open 3DS challenge returns the sameaction_required_tokenfor up to 1 hour, thenprocessingwith an empty token. - Same key, different Price Point or a custom charge: Returns
idempotency_key_reused. - No key: Every request is a new charge.
- Original request created no payment: A retry 10 minutes or more after it returns
failed.
Errors: These return HTTP 400 with a code, and nothing is charged:
idempotency_key_reused: The key belongs to a different purchase.idempotency_key_in_progress: Another request for this customer is running. Retry with the same key.saved_payment_method_rejected: The saved payment method is no longer valid. Ask the customer for new card details.
Rate limit: One-click requests are limited per saved payment method by one_click_limit (default: 5 per hour). Exceeding it returns HTTP 429.
curl --request POST \
--url https://billing.funnelfox.com/{org_id}/v1/checkout/one_click \
--header 'Content-Type: application/json' \
--data '
{
"external_id": "<string>",
"pp_ident": "<string>",
"client_metadata": {},
"initiation_type": null,
"cit_card_networks": null,
"idempotency_key": null
}
'import requests
url = "https://billing.funnelfox.com/{org_id}/v1/checkout/one_click"
payload = {
"external_id": "<string>",
"pp_ident": "<string>",
"client_metadata": {},
"initiation_type": None,
"cit_card_networks": None,
"idempotency_key": None
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
external_id: '<string>',
pp_ident: '<string>',
client_metadata: {},
initiation_type: null,
cit_card_networks: null,
idempotency_key: null
})
};
fetch('https://billing.funnelfox.com/{org_id}/v1/checkout/one_click', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://billing.funnelfox.com/{org_id}/v1/checkout/one_click",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'external_id' => '<string>',
'pp_ident' => '<string>',
'client_metadata' => [
],
'initiation_type' => null,
'cit_card_networks' => null,
'idempotency_key' => null
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://billing.funnelfox.com/{org_id}/v1/checkout/one_click"
payload := strings.NewReader("{\n \"external_id\": \"<string>\",\n \"pp_ident\": \"<string>\",\n \"client_metadata\": {},\n \"initiation_type\": null,\n \"cit_card_networks\": null,\n \"idempotency_key\": null\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://billing.funnelfox.com/{org_id}/v1/checkout/one_click")
.header("Content-Type", "application/json")
.body("{\n \"external_id\": \"<string>\",\n \"pp_ident\": \"<string>\",\n \"client_metadata\": {},\n \"initiation_type\": null,\n \"cit_card_networks\": null,\n \"idempotency_key\": null\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.funnelfox.com/{org_id}/v1/checkout/one_click")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"external_id\": \"<string>\",\n \"pp_ident\": \"<string>\",\n \"client_metadata\": {},\n \"initiation_type\": null,\n \"cit_card_networks\": null,\n \"idempotency_key\": null\n}"
response = http.request(request)
puts response.read_body{
"action_required_token": "<string>",
"checkout_status": "processing",
"failed_message_for_user": "<string>",
"order_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"decline_code": null,
"decline_code_source": "fakeintegration",
"idempotent_replay": false
}Path Parameters
Organization ID
Body
Your unique identifier for the user
256Unique identifier of the price point
256^[-A-Za-z_0-9]{1,256}$Custom metadata from your client application
How to send the payment: cit (customer-initiated) or mit (merchant-initiated). Leave it out to use the organization or funnel setting. Without the ff-secret-key header, this field can only make the setting stricter: mit is applied, cit is ignored where the setting is mit.
cit, mit Card brands allowed to go as CIT. Leave it out to use the organization or funnel setting. Without the ff-secret-key header, this list can only narrow the brands in the setting.
AMEX, CARTES_BANCAIRES, DINERS_CLUB, DISCOVER, EFTPOS, ELO, JCB, MAESTRO, MASTERCARD, MIR, UNIONPAY, VISA Unique key per purchase attempt. Reuse it on every retry of that attempt so the customer is charged only once.
1 - 128Response
Payment status and details
processing, succeeded, failed, cancelled fakeintegration, primer, stripe True when this response replays an earlier one-click request made with the same idempotency key, rather than a new charge.
Was this page helpful?
