Set up tokenization
Follow the Tokenization guide to load ZeroGateway.js and configure
your card fields. For the surcharge flow, modify the callback to fetch the rate instead of
sending the token directly to your charge endpoint:
let paymentToken = null ;
let enteredAmount = null ;
CollectJS. configure ({
fields: {
ccnumber: { selector: "#ccnumber" , placeholder: "Card number" },
ccexp: { selector: "#ccexp" , placeholder: "MM / YY" },
cvv: { selector: "#cvv" , placeholder: "CVV" },
},
callback : async function ( response ) {
paymentToken = response.token;
enteredAmount = parseInt (document. getElementById ( "amount" ).value);
await fetchRate (paymentToken, enteredAmount);
},
});
document. getElementById ( "payment-form" ). addEventListener ( "submit" , function ( e ) {
e. preventDefault ();
CollectJS. startPaymentRequest ();
});
Fetch the surcharge rate
After tokenization, send the token and amount to your server, which calls the Strictly /rate
endpoint:
async function fetchRate ( token , amount ) {
const res = await fetch ( `/api/rate?amount=${ amount }&paymentToken=${ token }` );
const { data } = await res. json ();
showReviewStep (data);
}
On your server. The example below uses Java, but this works the same in any server-side language:
RateController.java — Spring Boot
@ RestController
@ RequestMapping ( "/api" )
public class RateController {
private static final String RATE_URL =
"https://api.paywithzero.net/v1/public/payment/rate" ;
@ GetMapping ( "/rate" )
public ResponseEntity<Map< String , Object >> getRate (
@ RequestParam String amount ,
@ RequestParam String paymentToken ) throws Exception {
String credentials = Base64. getEncoder (). encodeToString (
(System. getenv ( "STRICTLY_EMAIL" ) + ":" + System. getenv ( "STRICTLY_PASSWORD" )). getBytes ()
);
String url = RATE_URL + "?amount=" + amount + "&paymentToken=" + paymentToken;
HttpRequest request = HttpRequest. newBuilder ()
. uri (URI. create (url))
. header ( "Authorization" , "Basic " + credentials)
. header ( "key-hash" , System. getenv ( "STRICTLY_KEY_HASH" ))
. GET ()
. build ();
HttpResponse< String > response = HttpClient. newHttpClient ()
. send (request, HttpResponse.BodyHandlers. ofString ());
return ResponseEntity. ok (
new ObjectMapper (). readValue (response. body (), Map.class)
);
}
}
The response:
{
"data" : {
"amount" : 1000 ,
"surcharge" : 35 ,
"totalAmount" : 1035 ,
"creditRate" : 3.5 ,
"debitRate" : 0 ,
"brand" : "visa" ,
"cardType" : "credit"
}
}
Debit cards typically return debitRate: 0 and surcharge: 0. Always use the surcharge and
totalAmount from the response — don't calculate them yourself.
Show the customer the breakdown
Display the rate data before the customer can confirm. This is required for surcharging compliance:
payment.js — show review step
function showReviewStep ( rateData ) {
document. getElementById ( "review-base" ).textContent = formatCents (rateData.amount);
document. getElementById ( "review-surcharge" ).textContent = formatCents (rateData.surcharge);
document. getElementById ( "review-total" ).textContent = formatCents (rateData.totalAmount);
document. getElementById ( "step-card" ).style.display = "none" ;
document. getElementById ( "step-review" ).style.display = "block" ;
}
function formatCents ( cents ) {
return new Intl. NumberFormat ( "en-US" , { style: "currency" , currency: "USD" }). format (cents / 100 );
}
< div id = "step-review" style = "display:none" >
< table >
< tr >< td >Base amount</ td > < td id = "review-base" ></ td ></ tr >
< tr >< td >Surcharge</ td > < td id = "review-surcharge" ></ td ></ tr >
< tr >< td >< strong >Total</ strong ></ td >< td id = "review-total" ></ td ></ tr >
</ table >
< button id = "btn-pay" >Pay</ button >
< button id = "btn-back" >Back</ button >
</ div >
Charge with the rate amounts
When the customer confirms, send the token and original amount to your server. Re-fetch /rate
server-side to get authoritative amounts, then charge using totalAmount:
payment.js — confirm payment
document. getElementById ( "btn-pay" ). addEventListener ( "click" , async () => {
const res = await fetch ( "/api/charge" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({ paymentToken, amount: enteredAmount }),
});
const result = await res. json ();
if (result.success) {
window.location.href = "/success" ;
} else {
alert ( "Payment failed: " + result.error);
}
});
The example below uses Java, but this works the same in any server-side language:
ChargeController.java — Spring Boot
@ RestController
@ RequestMapping ( "/api" )
public class ChargeController {
private static final String RATE_URL =
"https://api.paywithzero.net/v1/public/payment/rate" ;
private static final String CHARGE_URL =
"https://api.paywithzero.net/v1/public/payment/charge" ;
@ PostMapping ( "/charge" )
public ResponseEntity<Map< String , Object >> charge (
@ RequestBody ChargeRequest req ) throws Exception {
String credentials = Base64. getEncoder (). encodeToString (
(System. getenv ( "STRICTLY_EMAIL" ) + ":" + System. getenv ( "STRICTLY_PASSWORD" )). getBytes ()
);
// Re-fetch rate server-side — never trust client-supplied amounts
String rateUrl = RATE_URL
+ "?amount=" + req. getAmount ()
+ "&paymentToken=" + req. getPaymentToken ();
HttpRequest rateRequest = HttpRequest. newBuilder ()
. uri (URI. create (rateUrl))
. header ( "Authorization" , "Basic " + credentials)
. header ( "key-hash" , System. getenv ( "STRICTLY_KEY_HASH" ))
. GET ()
. build ();
HttpResponse< String > rateResponse = HttpClient. newHttpClient ()
. send (rateRequest, HttpResponse.BodyHandlers. ofString ());
Map< String , Object > rateBody =
new ObjectMapper (). readValue (rateResponse. body (), Map.class);
Map< String , Object > rate = (Map < String, Object > ) rateBody. get ( "data" );
Map< String , Object > body = Map. of (
"amount" , rate. get ( "totalAmount" ),
"card" , Map. of ( "paymentToken" , req. getPaymentToken ()),
"contact" , Map. of ( "email" , req. getEmail ()),
"billingAddress" , req. getBillingAddress (),
"order" , Map. of (
"amount" , rate. get ( "amount" ),
"shipping" , 0 , "tax" , 0 , "discount" , 0
),
"capture" , true
);
HttpRequest chargeRequest = HttpRequest. newBuilder ()
. uri (URI. create (CHARGE_URL))
. header ( "Authorization" , "Basic " + credentials)
. header ( "key-hash" , System. getenv ( "STRICTLY_KEY_HASH" ))
. header ( "Content-Type" , "application/json" )
. POST (HttpRequest.BodyPublishers. ofString (
new ObjectMapper (). writeValueAsString (body)))
. build ();
HttpResponse< String > chargeResponse = HttpClient. newHttpClient ()
. send (chargeRequest, HttpResponse.BodyHandlers. ofString ());
Map< String , Object > result = Map. of (
"success" , chargeResponse. statusCode () == 200 ,
"data" , new ObjectMapper (). readValue (chargeResponse. body (), Map.class)
);
return ResponseEntity. ok (result);
}
}