How To: Implement Chargeback Protection
Overview
Coinflow provides multiple ways to protect merchant accounts against fraud. Learn more about fraud prevention methods before you start implementing chargeback protection.
How to Add Chargeback Protection
When merchants opt in for chargeback protection, they need to install Coinflow Purchase Protection across every page of their site. This integration gathers device and session signals during the customer’s shopping experience so Coinflow can score the purchase for fraud and chargeback risk. Depending on your implementation method, select from the options below for setup instructions:
React
- Add the {‘
’} component to every page on your site. - Add your
merchantIdas a property to the {‘’} component. - In every {‘
’} component, add the chargebackProtectionDataproperty and input information about each purchase made via Coinflow. See Getting the Device ID if you call Coinflow APIs directly.
React Native
Coinflow provides a mobile module for both iOS and Android that exposes a getDeviceId method. Coinflow’s integrations team will share the module, sample diff files, and the application credentials needed to initialize it on sandbox and production.
Implementation
- Install the mobile module Coinflow provides and follow the README to wire it into your iOS and Android builds.
- Initialize the module on app launch using the application credentials supplied by Coinflow. The same credentials are used in sandbox and production.
- Call the module’s
getDeviceIdmethod to retrieve the device ID. - In
{"<CoinflowPurchase>"}add thechargebackProtectionDataproperty and add information about every purchase that is being made via Coinflow, and pass thedeviceIdthrough to the component. See Getting the Device ID below.
iOS (Native)
For native iOS apps that call Coinflow’s APIs directly (not through React Native), Coinflow uses nSure’s iOS SDK to gather device and session signals.
Implementation
-
Install the SDK using Swift Package Manager or CocoaPods:
Swift Package Manager — in Xcode, select File > Add Package Dependencies, enter
https://github.com/nsure-ai/ios-sdk, and choose version1.3.17or later.CocoaPods
pod 'nSure' -
Initialize the SDK on app launch using the
appIdandpartnerIdCoinflow’s integrations team provides. The same credentials are used on sandbox and production.import nSure NSure.sharedInstance(withAppID: "your-app-id", partherID: "your-partner-id")#import <nSure/nSure.h> [NSure sharedInstanceWithAppID:@"your-app-id" partherID:@"your-partner-id"]; -
In
{"<CoinflowPurchase>"}(or your direct API calls), add thechargebackProtectionDataproperty and pass the retrieveddeviceIdthrough. See Getting the Device ID below.
API
- Add the following code to the
<head>of every page on your site — not just the checkout page. After completing the chargeback protection questionnaire, Coinflow will provide your productionpartnerId. UseCOINFTESTon sandbox.
{"<script src=\"https://sdk.nsureapi.com/sdk.js\"> </script>"}
<script>
window.nSureAsyncInit = function(deviceId) {
window.nSureSDK.init({
appId: '9JBW2RHC7JNJN8ZQ', // Remains the same on sandbox and prod
partnerId: 'COINFTEST' // Use COINFTEST on sandbox. Coinflow assigns your prod partnerId
});
};
</script>
📘 This script gathers information about the user’s device, how they interact with your website, and other signals that let Coinflow’s models predict the risk of fraud or chargeback for this particular user.
- To each of your API calls, add the
x-device-idheader. Pass the value returned by callingwindow?.nSureSDK?.getDeviceId()on your website. See Getting the Device ID below.
This
deviceIdis how Coinflow ties an individual request back to the device and session signals collected by the script you installed above.
- In the Checkout Endpoint and the Redeem Transaction Endpoint, pass the
chargebackProtectionData.
📘 This information describes what is being purchased, which lets Coinflow’s models determine the risk of chargeback for this particular purchase.
Getting the Device ID
When chargeback protection is enabled, pass the device ID in the x-device-id header on checkout, redeem, and subscription API calls. How you retrieve it depends on your integration:
API
After the protection script has loaded and initialized on the page, read the device ID before making your Coinflow API request:
const deviceId = window?.nSureSDK?.getDeviceId();
// Include on Coinflow API requests
headers: {
'x-device-id': deviceId,
}
React
Mount <CoinflowPurchaseProtection> on every page, then use the useCoinflowProtectionHeaders hook from @coinflowlabs/react:
import {
CoinflowPurchaseProtection,
useCoinflowProtectionHeaders,
} from '@coinflowlabs/react';
function App() {
return (
<>
<CoinflowPurchaseProtection
coinflowEnv="sandbox"
merchantId="your-merchant-id"
/>
{/* Your app content */}
</>
);
}
function Checkout() {
const getProtectionHeaders = useCoinflowProtectionHeaders();
const deviceId = getProtectionHeaders()['x-device-id'];
// Include on Coinflow API requests
headers: {
...getProtectionHeaders(),
}
}
If you use <CoinflowPurchase>, the device ID is sent automatically — you only need to retrieve it manually when calling Coinflow APIs directly from your backend-for-frontend or custom checkout flow.
React Native
Coinflow provides a native module for iOS and Android. Initialize it on app launch using the credentials Coinflow supplies, then call getDeviceId and pass the result to <CoinflowPurchase> or your API layer:
import { CoinflowPurchase } from '@coinflowlabs/react-native';
// After initializing the native module at app launch:
const deviceId = await nativeModule.getDeviceId();
<CoinflowPurchase
deviceId={deviceId}
chargebackProtectionData={[/* ... */]}
{/* other props */}
/>
Contact Coinflow’s integrations team for the module, sample diff files, and initialization credentials.
iOS (Native)
After initializing the SDK, read the device ID:
let deviceId = NSure.sharedInstance.deviceId
Pass this value to your merchant server so it can be included as the x-device-id header on Coinflow API requests.
What to pass into chargebackProtectionData
Merchants that opt-in for chargeback protection are required to pass chargebackProtectionData as a prop to the <CoinflowPurchase> component or to our card checkout, saved card checkout, ach checkout , and redeem transaction endpoints.
🚧 The more information that you pass here the better the authorization rates will be, so it is in your best interest to supply as much information as possible
Example of chargebackProtectionData data structure
{
/**
* The name of the product
*/
productName: string;
/**
* The product type. Possible values include: inGameProduct, gameOfSkill, dataStorage, computingResources, sportsTicket, eSportsTicket, musicTicket, conferenceTicket, virtualSportsTicket, virtualESportsTicket, virtualMusicTicket, virtualConferenceTicket, alcohol, DLC, subscription, fundACause, realEstate, computingContract, digitalArt, topUp
* Contact Coinflow for the productType value.
*/
productType: 'inGameProduct' |
'gameOfSkill' |
'dataStorage' |
'computingResources' |
'sportsTicket' |
'eSportsTicket' |
'musicTicket' |
'conferenceTicket' |
'virtualSportsTicket' |
'virtualESportsTicket' |
'virtualMusicTicket' |
'virtualConferenceTicket' |
'alcohol' |
'DLC' |
'subscription' |
'fundACause' |
'realEstate' |
'computingContract' |
'digitalArt' |
'topUp';
/**
* The number of units sold
*/
quantity: number;
/**
* Any additional data that the store can provide on the product, e.g. description, link to image, etc.
*/
rawProductData?: { [key: string]: any };
}
Travel bookings
Merchants selling flights and hotel reservations should declare each booking with a travel-specific itemClass so the purchase is evaluated with travel-specific fraud signals — itinerary, lodging, PNR, trip, and flight details — instead of being treated as a generic product. Choose the item class that matches what is being sold:
itemClass |
Use for |
|---|---|
travel |
A combined flight + hotel package |
flightTicket |
A flight ticket on its own |
flightUpgrade |
An upgrade to an existing flight booking (seats, baggage, fare changes, insurance) |
lodging |
A hotel reservation on its own |
If itemClass is omitted, the cart item is treated as a generic product and travel-specific fraud signals are not evaluated, which can reduce approval rates for travel transactions.
Travel package data structure (itemClass: 'travel')
{
/**
* Identifies this cart item as a travel booking
*/
itemClass: 'travel';
/**
* The cart item's unique ID
*/
id?: string;
/**
* The item's selling price
*/
sellingPrice: {
valueInCurrency: number; // e.g. 1250.00
currency: string; // ISO 4217, e.g. 'USD'
};
/**
* The item's list price
*/
listPrice?: {
valueInCurrency: number;
currency: string;
};
/**
* The lodging details of the booking
*/
lodging: {
reservationId: string; // Hotel reservation ID, e.g. 'X36Q9C'
nights: number; // Number of nights booked (min 1)
checkInDate: string; // Check-in date, e.g. '2026-09-02'
checkInHour: string; // Hour of day of check-in, '00'-'23'
hotel: {
name: string; // e.g. 'The Plaza Hotel'
country: string; // Two-letter ISO 3166 code, e.g. 'US'
city: string; // e.g. 'New York'
};
wereExtrasPurchased: boolean; // Whether extras were purchased
isRefundable: boolean; // Whether the reservation is refundable
highestRoomType: string; // Most luxurious room booked, e.g. 'Presidential Suite'
numberOfRooms: number; // Rooms booked (min 1)
numberOfGuests: number; // Guests in the reservation (min 1)
numberOfKids: number; // Kids out of the number of guests (min 0)
};
/**
* The flight ticket's PNR code
*/
pnr: string; // e.g. 'X36Q9C'
/**
* The trip details
*/
trip: {
numberOfFlights: number; // Flights in the trip (min 1)
numberOfCheckedBags: number; // Checked bags (min 0)
firstWayDestination: string; // Three-letter IATA airport code, e.g. 'JFK'
isRoundTrip: boolean; // true if the trip is a round-trip
highestClass: 'economy' | 'luxurious'; // 'luxurious' if the highest travel class is business/first
isFlexibleTicket: boolean; // true if the ticket is flexible
wereExtrasPurchased: boolean; // true if extras were included
};
/**
* The first flight of the trip
*/
firstFlight: {
numberOfTravelers: number; // Travelers on the first flight (min 1)
departureTime: number; // Departure timestamp in *milliseconds* since the unix epoch
departureAirport: string; // Three-letter IATA code, e.g. 'JFK'
duration: number; // Flight duration in hours, e.g. 3.5
flightNumber: string; // Alphanumeric flight code, e.g. 'BA2491A'
};
/**
* Any additional data that the store can provide on the booking
*/
rawProductData?: { [key: string]: any };
}
Flight-only bookings (itemClass: 'flightTicket')
For a flight ticket sold without lodging, use itemClass: 'flightTicket'. It has the same required pnr, trip, and firstFlight fields as the travel package above, but no lodging:
{
itemClass: 'flightTicket';
id?: string;
sellingPrice: {valueInCurrency: number, currency: string};
listPrice?: {valueInCurrency: number, currency: string};
pnr: string; // The flight ticket's PNR code
trip: {/* same shape as travel */};
firstFlight: {/* same shape as travel */};
rawProductData?: { [key: string]: any };
}
Flight upgrades (itemClass: 'flightUpgrade')
For add-ons to an existing flight booking — seat selection, extra baggage, fare changes, or travel insurance — use itemClass: 'flightUpgrade':
{
itemClass: 'flightUpgrade';
id?: string;
sellingPrice: {valueInCurrency: number, currency: string};
listPrice?: {valueInCurrency: number, currency: string};
/**
* The PNR code of the booking being upgraded
*/
pnr?: string;
/**
* The type of upgrade
*/
upgradeType: 'changes' | 'fareIncrease' | 'baggage' | 'seats' | 'insurance' | 'other';
/**
* The upgrade description — required when upgradeType is 'other'
*/
description?: string;
rawProductData?: { [key: string]: any };
}
Hotel-only bookings (itemClass: 'lodging')
For a hotel reservation sold without flights, use itemClass: 'lodging'. The reservation fields sit at the top level of the item (the same fields as the lodging object of the travel package):
{
itemClass: 'lodging';
id?: string;
sellingPrice: {valueInCurrency: number, currency: string};
listPrice?: {valueInCurrency: number, currency: string};
reservationId: string; // e.g. 'X36Q9C'
nights: number; // min 1
checkInDate: string; // e.g. '2026-09-02'
checkInHour: string; // '00'-'23'
hotel: {
name: string;
country: string; // Two-letter ISO 3166 code
city: string;
};
wereExtrasPurchased: boolean;
isRefundable: boolean;
highestRoomType: string;
numberOfRooms: number; // min 1
numberOfGuests: number; // min 1
numberOfKids: number; // min 0
rawProductData?: { [key: string]: any };
}
Example travel booking
The values below are examples only — replace them with the real details of each booking.
{
"itemClass": "travel",
"id": "5de33332-546a-4171-8988-2a43d2bfe9c6",
"sellingPrice": {
"valueInCurrency": 1250.00,
"currency": "USD"
},
"lodging": {
"reservationId": "X36Q9C",
"nights": 3,
"checkInDate": "2026-09-02",
"checkInHour": "15",
"hotel": {
"name": "The Plaza Hotel",
"country": "US",
"city": "New York"
},
"wereExtrasPurchased": false,
"isRefundable": true,
"highestRoomType": "Deluxe King",
"numberOfRooms": 1,
"numberOfGuests": 2,
"numberOfKids": 0
},
"pnr": "X36Q9C",
"trip": {
"numberOfFlights": 2,
"numberOfCheckedBags": 1,
"firstWayDestination": "JFK",
"isRoundTrip": true,
"highestClass": "economy",
"isFlexibleTicket": false,
"wereExtrasPurchased": false
},
"firstFlight": {
"numberOfTravelers": 2,
"departureTime": 1788373800000,
"departureAirport": "SFO",
"duration": 5.5,
"flightNumber": "AA1234"
},
"rawProductData": {
"packageName": "NYC Getaway — Flight + Hotel",
"bookingChannel": "web"
}
}
Example Implementation on Coinflow’s Prebuilt UI
<CoinflowPurchase
wallet={wallet}
merchantId={process.env.REACT_APP_MERCHANT_ID as string}
transaction={transaction}
amount={amount}
chargebackProtectionData={[{
"productName": "Sword", // Name of Product
"productType": "inGameProduct", // Get the value from Coinflow
"quantity": 1,
"rawProductData": { // Adjust based on the available product data
"productID": "sword12345",
"productDescription": "A legendary sword with magical powers.",
"productCategory": "Weapon",
"weight": "15 lbs",
"dimensions": "40 in x 5 in",
"origin": "Ancient Kingdom",
"craftedBy": "Master Blacksmith",
"craftingDate": "2024-06-19"
}
},]}
/>
Example Implementation on Coinflow’s APIs
curl --request POST \
--url https://api-sandbox.coinflow.cash/api/checkout/ach/merchantId \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"subtotal": {
"cents": 100
},
"token": "5a000000-0000-0000-0000-000000000000",
"chargebackProtectionData": [
{
"productType": "inGameProduct",
"productName": "Sword",
"quantity": 1,
"rawProductData": {
"productID": "sword12345",
"productDescription": "A legendary sword with magical powers.",
"productCategory": "Weapon",
"weight": "15 lbs",
"dimensions": "40 in x 5 in",
"origin": "Ancient Kingdom",
"craftedBy": "Master Blacksmith",
"craftingDate": "2024-06-19"
}
}
]
}
'