Tutorials
Card payments
Learn how to implement card payments with Blnk.
A card payment reserves the amount when the network authorizes it, then takes the money when the processor settles it. The hold is an inflight transaction, and settlement commits that hold.
This tutorial shows how to implement that with Blnk.
Step
Design the money movement
Before we start building, we need to define our money movement. This helps us outline how a card payment leaves Alice’s balance.
Alice pays $20.00 to a merchant. The source is her card balance, and the destination is @WorldUSD, an internal balance for money that leaves the system.
We record the payment with inflight set, so the $20.00 stays reserved until the processor settles it. Her available balance drops by $20.00 while the hold is open.
Step
Create a ledger
Before we start recording transactions, we need to set up our ledger to track customers and their card balances.
For our card example, we only support one currency, USD. Therefore, we’ll create a ledger to manage all USD card balances belonging to our customers.
Create the ledger:
curl -X POST "CORE_URL/ledgers" \
-H "X-blnk-key: CORE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "{{name}}",
"meta_data": {
"description": "{{description}}"
}
}'const response = await blnk.Ledgers.create({
name: '{{name}}',
meta_data: {
description: '{{description}}',
},
});ledger, _, _ := client.Ledger.Create(blnkgo.CreateLedgerRequest{
Name: "{{name}}",
MetaData: blnkgo.MetaData{
"description": "{{description}}",
},
})response = blnk.ledgers.create({
"name": "{{name}}",
"meta_data": {
"description": "{{description}}",
},
})ApiResponse<JsonNode> response = blnk.ledgers().create(
CreateLedger.create()
.name("{{name}}")
.metaData(Map.of(
"description", "{{description}}"
)));Step
Create an identity
An identity is how we record the customer. We pass Alice’s type, name, email, and phone number.
Anything else we want to keep goes in metadata. We’ll store her customer id there as customer_id.
Create the identity:
curl -X POST "CORE_URL/identities" \
-H "X-blnk-key: CORE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"identity_type": "individual",
"first_name": "{{first_name}}",
"last_name": "{{last_name}}",
"email_address": "{{email_address}}",
"phone_number": "{{phone_number}}",
"meta_data": {
"customer_id": "{{customer_id}}"
}
}'const response = await blnk.Identity.create({
identity_type: 'individual',
first_name: '{{first_name}}',
last_name: '{{last_name}}',
email_address: '{{email_address}}',
phone_number: '{{phone_number}}',
meta_data: {
customer_id: '{{customer_id}}',
},
});identity, _, _ := client.Identity.Create(blnkgo.Identity{
IdentityType: blnkgo.Individual,
FirstName: "{{first_name}}",
LastName: "{{last_name}}",
EmailAddress: "{{email_address}}",
PhoneNumber: "{{phone_number}}",
MetaData: blnkgo.MetaData{
"customer_id": "{{customer_id}}",
},
})response = blnk.identity.create({
"identity_type": "individual",
"first_name": "{{first_name}}",
"last_name": "{{last_name}}",
"email_address": "{{email_address}}",
"phone_number": "{{phone_number}}",
"meta_data": {
"customer_id": "{{customer_id}}",
},
})ApiResponse<JsonNode> response = blnk.identity().create(
IdentityData.create()
.identityType("individual")
.firstName("{{first_name}}")
.lastName("{{last_name}}")
.emailAddress("{{email_address}}")
.phoneNumber("{{phone_number}}")
.metaData(Map.of("customer_id", "{{customer_id}}")));Step
Create the card balance
Now that we have Alice in our ledger, we can create a balance for her card, stored in the ledger from step 2.
We’ll use the ledger_id and identity_id from the earlier steps, set the currency, and submit. Blnk creates the balance and attaches it to Alice.
Create the card balance:
curl -X POST "CORE_URL/balances" \
-H "X-blnk-key: CORE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"ledger_id": "{{ledger_id}}",
"identity_id": "{{identity_id}}",
"currency": "{{currency}}"
}'const response = await blnk.LedgerBalances.create({
ledger_id: '{{ledger_id}}',
identity_id: '{{identity_id}}',
currency: '{{currency}}',
});balance, _, _ := client.LedgerBalance.Create(blnkgo.CreateLedgerBalanceRequest{
LedgerID: "{{ledger_id}}",
IdentityID: "{{identity_id}}",
Currency: "{{currency}}",
})response = blnk.ledger_balances.create({
"ledger_id": "{{ledger_id}}",
"identity_id": "{{identity_id}}",
"currency": "{{currency}}",
})ApiResponse<JsonNode> response = blnk.ledgerBalances().create(
CreateLedgerBalance.create()
.ledgerId("{{ledger_id}}")
.identityId("{{identity_id}}")
.currency("{{currency}}"));Step
Load the card
Alice loads $100.00 onto the card before she spends. The source is @WorldUSD, and the destination is her card balance.
The amount is a decimal, and we multiply it by precision 100 to get precise_amount. The reference and the description are already set.
Load the card:
curl -X POST "CORE_URL/transactions" \
-H "X-blnk-key: CORE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"precise_amount": {{precise_amount}},
"precision": 100,
"reference": "CRD-DEP-20261019-103000",
"description": "Card load",
"currency": "{{currency}}",
"source": "{{source}}",
"destination": "{{destination}}",
"allow_overdraft": true
}'const amount = {{amount}};
const precision = 100;
const precise_amount = Math.round(amount * precision);
const response = await blnk.Transactions.create({
precise_amount,
precision,
reference: 'CRD-DEP-20261019-103000',
description: 'Card load',
currency: '{{currency}}',
source: '{{source}}',
destination: '{{destination}}',
allow_overdraft: true,
});amount := {{amount}}
precision := 100
preciseAmount := big.NewInt(int64(math.Round(amount * float64(precision))))
transaction, _, _ := client.Transaction.Create(blnkgo.CreateTransactionRequest{
ParentTransaction: blnkgo.ParentTransaction{
PreciseAmount: preciseAmount,
Precision: precision,
Reference: "CRD-DEP-20261019-103000",
Description: "Card load",
Currency: "{{currency}}",
Source: "{{source}}",
Destination: "{{destination}}",
},
AllowOverdraft: true,
})amount = {{amount}}
precision = 100
precise_amount = round(amount * precision)
response = blnk.transactions.create({
"precise_amount": precise_amount,
"precision": precision,
"reference": "CRD-DEP-20261019-103000",
"description": "Card load",
"currency": "{{currency}}",
"source": "{{source}}",
"destination": "{{destination}}",
"allow_overdraft": True,
})double amount = {{amount}};
int precision = 100;
int preciseAmount = (int) Math.round(amount * precision);
ApiResponse<JsonNode> response = blnk.transactions().create(
CreateTransactions.create()
.preciseAmount(preciseAmount)
.precision(precision)
.reference("CRD-DEP-20261019-103000")
.description("Card load")
.currency("{{currency}}")
.source("{{source}}")
.destination("{{destination}}")
.allowOverdraft(true));Step
Authorize the payment
The network asks whether Alice can cover $20.00. We record an inflight transaction from her card balance to @WorldUSD, which reserves the amount without applying it yet.
While the hold is open, that $20.00 is not available for another payment. A new transaction larger than the remaining $80.00 is rejected.
Authorize the payment:
curl -X POST "CORE_URL/transactions" \
-H "X-blnk-key: CORE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"precise_amount": {{precise_amount}},
"precision": 100,
"reference": "CRD-20261019-103000",
"description": "Card payment",
"currency": "{{currency}}",
"source": "{{source}}",
"destination": "{{destination}}",
"inflight": true
}'const amount = {{amount}};
const precision = 100;
const precise_amount = Math.round(amount * precision);
const response = await blnk.Transactions.create({
precise_amount,
precision,
reference: 'CRD-20261019-103000',
description: 'Card payment',
currency: '{{currency}}',
source: '{{source}}',
destination: '{{destination}}',
inflight: true,
});amount := {{amount}}
precision := 100
preciseAmount := big.NewInt(int64(math.Round(amount * float64(precision))))
transaction, _, _ := client.Transaction.Create(blnkgo.CreateTransactionRequest{
ParentTransaction: blnkgo.ParentTransaction{
PreciseAmount: preciseAmount,
Precision: precision,
Reference: "CRD-20261019-103000",
Description: "Card payment",
Currency: "{{currency}}",
Source: "{{source}}",
Destination: "{{destination}}",
},
Inflight: true,
})amount = {{amount}}
precision = 100
precise_amount = round(amount * precision)
response = blnk.transactions.create({
"precise_amount": precise_amount,
"precision": precision,
"reference": "CRD-20261019-103000",
"description": "Card payment",
"currency": "{{currency}}",
"source": "{{source}}",
"destination": "{{destination}}",
"inflight": True,
})double amount = {{amount}};
int precision = 100;
int preciseAmount = (int) Math.round(amount * precision);
ApiResponse<JsonNode> response = blnk.transactions().create(
CreateTransactions.create()
.preciseAmount(preciseAmount)
.precision(precision)
.reference("CRD-20261019-103000")
.description("Card payment")
.currency("{{currency}}")
.source("{{source}}")
.destination("{{destination}}")
.inflight(true));Step
Commit the payment
The processor settles the payment, so we commit the hold. We send commit for the transaction from the previous step, and the $20.00 leaves Alice’s card.
Commit the payment:
curl -X PUT "CORE_URL/transactions/inflight/{{transaction_id}}" \
-H "X-blnk-key: CORE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "commit"
}'const response = await blnk.Transactions.updateStatus(
'{{transaction_id}}',
{
status: 'commit',
},
);transaction, _, _ := client.Transaction.Update(
"{{transaction_id}}",
blnkgo.UpdateStatus{
Status: blnkgo.InflightStatusCommit,
},
)response = blnk.transactions.update_status(
"{{transaction_id}}",
{
"status": "commit",
},
)ApiResponse<JsonNode> response = blnk.transactions().updateStatus(
"{{transaction_id}}",
UpdateTransactionStatus.create()
.status("commit"));Step
Confirmation
The commit is queued. When it is applied, Alice’s card balance is $80.00.
If you have a Blnk Cloud account or an Enterprise license, you can confirm this by checking the dashboard or using the CLI.
Print every balance:
blnk balances list