Bayarcash 1.0.0
dotnet add package Bayarcash --version 1.0.0
NuGet\Install-Package Bayarcash -Version 1.0.0
<PackageReference Include="Bayarcash" Version="1.0.0" />
<PackageVersion Include="Bayarcash" Version="1.0.0" />
<PackageReference Include="Bayarcash" />
paket add Bayarcash --version 1.0.0
#r "nuget: Bayarcash, 1.0.0"
#:package Bayarcash@1.0.0
#addin nuget:?package=Bayarcash&version=1.0.0
#tool nuget:?package=Bayarcash&version=1.0.0
Bayarcash Payment Gateway .NET SDK
The Bayarcash SDK provides an expressive, idiomatic interface for interacting with Bayarcash's Payment Gateway API from .NET. It supports both API v2 (default) and v3, with additional query features available in v3. It is a feature-parity port of the official Bayarcash PHP SDK.
Table of Contents
- Requirements
- Installation
- Getting Started
- Configuration
- Quick Start: Accept a Payment
- Payment Channels
- Creating a Payment Intent
- Handling Callbacks
- Payment & Transaction Status
- Transactions
- FPX Direct Debit
- Manual Bank Transfer
- Portals & FPX Banks
- Error Handling
- Response Objects
- Dependency Injection
- Security Recommendations
- Support
Requirements
- .NET targeting
netstandard2.0or newer (works on .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5/6/7/8+). - No third-party runtime dependencies — the SDK uses only
System.Net.Http,System.Security.Cryptography, andSystem.Text.Json.
You will need two credentials from your Bayarcash console:
- API token — used to authenticate SDK requests.
- API secret key — used to generate request checksums and verify callbacks.
Installation
dotnet add package Bayarcash
Getting Started
using Bayarcash;
using var bayarcash = new BayarcashClient("YOUR_API_TOKEN");
bayarcash.UseSandbox(); // remove this line in production
You may also construct the client with options:
using var bayarcash = new BayarcashClient(
"YOUR_API_TOKEN",
new BayarcashOptions
{
Sandbox = true, // switch to the sandbox environment
ApiVersion = "v3", // "v2" (default) or "v3"
Timeout = 60, // request timeout in seconds (default 30)
});
Configuration
The fluent setters mirror the PHP SDK and can be chained. Call them before making
requests, and omit UseSandbox() in production to hit the live gateway.
bayarcash
.UseSandbox() // switch to the sandbox environment
.SetApiVersion("v3") // "v2" (default) or "v3"
.SetTimeout(60); // request timeout in seconds
bayarcash.GetApiVersion(); // read back the current version
Quick Start: Accept a Payment
A complete FPX payment flow, from creating the payment to verifying the result:
using Bayarcash;
using var bayarcash = new BayarcashClient("YOUR_API_TOKEN");
bayarcash.UseSandbox();
const string apiSecretKey = "YOUR_API_SECRET_KEY";
// 1. Build the payment request
var data = new Dictionary<string, object?>
{
["portal_key"] = "your_portal_key",
["payment_channel"] = PaymentChannel.Fpx,
["order_number"] = "INV-1001",
["amount"] = "10.00",
["payer_name"] = "Ahmad bin Abdullah",
["payer_email"] = "ahmad@example.com",
["payer_telephone_number"] = "0123456789",
["return_url"] = "https://your-site.com/payment/return",
["callback_url"] = "https://your-site.com/payment/callback",
};
// 2. Sign it (recommended)
data["checksum"] = bayarcash.CreatePaymentIntentChecksumValue(apiSecretKey, data);
// 3. Create the payment intent and redirect the payer to Bayarcash
var paymentIntent = await bayarcash.CreatePaymentIntentAsync(data);
return Redirect(paymentIntent.Url!);
After payment, Bayarcash calls your callback_url (server-to-server) and redirects the payer
to your return_url. Verify both — see Handling Callbacks.
Payment Channels
Pass one of these constants (or an array of them) as payment_channel:
PaymentChannel.Fpx // 1 FPX Online Banking
PaymentChannel.ManualTransfer // 2 Manual Bank Transfer
PaymentChannel.FpxDirectDebit // 3 FPX Direct Debit
PaymentChannel.FpxLineOfCredit // 4 FPX Line of Credit
PaymentChannel.DuitNowDobw // 5 DuitNow Online Banking
PaymentChannel.DuitNowQr // 6 DuitNow QR
PaymentChannel.SPayLater // 7 ShopeePayLater
PaymentChannel.BoostPayFlex // 8 Boost PayFlex
PaymentChannel.QrisOb // 9 QRIS Online Banking
PaymentChannel.QrisWallet // 10 QRIS Wallet
PaymentChannel.Nets // 11 NETS
PaymentChannel.CreditCard // 12 Credit Card
PaymentChannel.Alipay // 13 Alipay
PaymentChannel.WeChatPay // 14 WeChat Pay
PaymentChannel.PromptPay // 15 PromptPay
PaymentChannel.TouchNGo // 16 Touch 'n Go eWallet
PaymentChannel.BoostWallet // 17 Boost Wallet
PaymentChannel.GrabPay // 18 GrabPay
PaymentChannel.GrabPayLater // 19 Grab PayLater
PaymentChannel.ShopeePay // 21 ShopeePay (note: there is no channel id 20)
Creating a Payment Intent
var paymentIntent = await bayarcash.CreatePaymentIntentAsync(data);
Request fields:
| Field | Required | Description |
|---|---|---|
portal_key |
Yes | Your portal key. |
order_number |
Yes | Your reference. Max 30 chars. |
amount |
Yes | String with up to 2 decimals, e.g. "10.00". |
payer_name |
Yes | Max 150 chars. |
payer_email |
Yes | Valid email, max 250 chars. |
payment_channel |
No | A PaymentChannel id, or an array of ids. If omitted, the payer chooses on the Bayarcash page. |
payer_telephone_number |
No | Required for e-wallet / DuitNow channels. |
return_url |
No | Where the payer's browser is redirected after payment. |
callback_url |
No | Server-to-server notification URL. |
metadata |
No | Any extra data you want echoed back. |
checksum |
No | Recommended. See below. |
Checksum
The checksum protects the request from tampering. Generate it after building the request
and append it as checksum:
data["checksum"] = bayarcash.CreatePaymentIntentChecksumValue(apiSecretKey, data);
The checksum is an HMAC-SHA256 (lowercase hex) computed from payment_channel,
order_number, amount, payer_name, and payer_email.
Handling Callbacks
Bayarcash sends two kinds of notification. Always verify them with your API secret key
before trusting the data. Each verifier returns true only when the checksum matches, using a
constant-time comparison.
| Notification | How it arrives | Read it from |
|---|---|---|
callback_url (transaction) |
Server-to-server POST (form-encoded) | Request form |
return_url (payer redirect) |
Browser redirect — POST on v2, GET query on v3 | Request form / query |
// callbackData is an IReadOnlyDictionary<string, string?> built from the request form/query.
var callbackData = Request.Form.ToDictionary(kv => kv.Key, kv => (string?)kv.Value);
// Transaction callback (sent to your callback_url)
if (bayarcash.VerifyTransactionCallbackData(callbackData, apiSecretKey))
{
// Data is authentic — safe to process.
}
// Payer redirect (sent to your return_url)
if (bayarcash.VerifyReturnUrlCallbackData(callbackData, apiSecretKey))
{
// ...
}
// Pre-transaction callback (sent before the transaction record)
if (bayarcash.VerifyPreTransactionCallbackData(callbackData, apiSecretKey))
{
// ...
}
See FPX Direct Debit for mandate-specific callback verifiers.
Payment & Transaction Status
Transaction status is an integer code. Use the Fpx helper instead of hardcoding numbers:
using Bayarcash;
Fpx.StatusNew; // 0
Fpx.StatusPending; // 1
Fpx.StatusFailed; // 2
Fpx.StatusSuccess; // 3
Fpx.StatusCancelled; // 4
if (int.Parse(callbackData["status"]!) == Fpx.StatusSuccess)
{
// Payment successful
}
Fpx.GetStatusText(3); // "Successful"
Transactions
// Get a single transaction (v2 and v3)
var transaction = await bayarcash.GetTransactionAsync("transaction_id");
The following query helpers require API v3 and throw InvalidOperationException on v2:
bayarcash.SetApiVersion("v3");
var result = await bayarcash.GetAllTransactionsAsync(new Dictionary<string, object?>
{
["order_number"] = "INV-1001",
["status"] = "3",
["payment_channel"] = PaymentChannel.Fpx,
["exchange_reference_number"] = "REF123",
["payer_email"] = "ahmad@example.com",
});
// result.Data => IReadOnlyList<Transaction>, result.Meta => pagination metadata (JsonElement)
var byOrder = await bayarcash.GetTransactionByOrderNumberAsync("INV-1001");
var byEmail = await bayarcash.GetTransactionsByPayerEmailAsync("ahmad@example.com");
var byStatus = await bayarcash.GetTransactionsByStatusAsync("3");
var byChannel = await bayarcash.GetTransactionsByPaymentChannelAsync(PaymentChannel.Fpx);
var byRef = await bayarcash.GetTransactionByReferenceNumberAsync("REF123"); // single or null
// Get a payment intent by id (v3 only)
var intent = await bayarcash.GetPaymentIntentAsync("payment_intent_id");
// Cancel a payment intent (v3 only)
await bayarcash.CancelPaymentIntentAsync("payment_intent_id");
FPX Direct Debit
FPX Direct Debit lets you set up a recurring mandate and later maintain or terminate it.
Constants live on the FpxDirectDebit class:
using Bayarcash;
// Payer ID type
FpxDirectDebit.Nric; // 1 (New IC)
FpxDirectDebit.OldIc; // 2
FpxDirectDebit.Passport; // 3
FpxDirectDebit.BusinessRegistration; // 4
FpxDirectDebit.Others; // 5
// Frequency mode
FpxDirectDebit.ModeDaily; // "DL"
FpxDirectDebit.ModeWeekly; // "WK"
FpxDirectDebit.ModeMonthly; // "MT"
FpxDirectDebit.ModeYearly; // "YR"
1. Enrolment
var data = new Dictionary<string, object?>
{
["portal_key"] = "your_portal_key",
["order_number"] = "DD-1001",
["amount"] = "10.00",
["payer_name"] = "Ahmad bin Abdullah",
["payer_id_type"] = FpxDirectDebit.Nric,
["payer_id"] = "900101011234",
["payer_email"] = "ahmad@example.com",
["payer_telephone_number"] = "0123456789",
["application_reason"] = "Monthly subscription",
["frequency_mode"] = FpxDirectDebit.ModeMonthly,
["effective_date"] = "2026-08-01", // optional, yyyy-MM-dd
["expiry_date"] = "2027-08-01", // optional, yyyy-MM-dd
["return_url"] = "https://your-site.com/mandate/return",
};
data["checksum"] = bayarcash.CreateFpxDirectDebitEnrolmentChecksumValue(apiSecretKey, data);
var mandate = await bayarcash.CreateFpxDirectDebitEnrollmentAsync(data);
return Redirect(mandate.Url!); // redirect payer to the enrolment page
2. Maintenance
var data = new Dictionary<string, object?>
{
["amount"] = "15.00",
["payer_email"] = "ahmad@example.com",
["payer_telephone_number"] = "0123456789",
["application_reason"] = "Update amount",
["frequency_mode"] = FpxDirectDebit.ModeMonthly,
};
data["checksum"] = bayarcash.CreateFpxDirectDebitMaintenanceChecksumValue(apiSecretKey, data);
var mandate = await bayarcash.CreateFpxDirectDebitMaintenanceAsync(mandateId, data);
return Redirect(mandate.Url!);
3. Termination
var mandate = await bayarcash.CreateFpxDirectDebitTerminationAsync(mandateId, new Dictionary<string, object?>
{
["application_reason"] = "Customer cancelled",
});
return Redirect(mandate.Url!);
Retrieving mandates & verifying mandate callbacks
var mandate = await bayarcash.GetFpxDirectDebitAsync(mandateId);
var transaction = await bayarcash.GetFpxDirectDebitTransactionAsync(transactionId);
// Mandate callback verifiers
bayarcash.VerifyDirectDebitBankApprovalCallbackData(callbackData, apiSecretKey);
bayarcash.VerifyDirectDebitAuthorizationCallbackData(callbackData, apiSecretKey);
bayarcash.VerifyDirectDebitTransactionCallbackData(callbackData, apiSecretKey);
Manual Bank Transfer
Submit a manual (offline) bank transfer with proof of payment. The proof file is uploaded as multipart form data.
var response = await bayarcash.CreateManualBankTransferAsync(new Dictionary<string, object?>
{
["portal_key"] = "your_portal_key",
["payment_gateway"] = PaymentChannel.ManualTransfer, // must be 2
["order_no"] = "MT-1001",
["buyer_name"] = "Ahmad bin Abdullah",
["buyer_email"] = "ahmad@example.com",
["buyer_tel_no"] = "0123456789", // optional
["order_amount"] = "10.00",
["merchant_bank_name"] = "Maybank",
["merchant_bank_account"] = "1234567890",
["merchant_bank_account_holder"] = "Your Company Sdn Bhd",
["bank_transfer_type"] = "Internet Banking", // or "Cash Deposit Machine (CDM)"
["bank_transfer_notes"] = "Payment for order MT-1001",
["bank_transfer_date"] = "2026-07-22", // optional, defaults to today
["proof_of_payment"] = "/path/to/receipt.jpg", // jpeg/png/gif/pdf
});
if (response.Success)
{
// response.HtmlForm is an auto-submit form; response.FormData / response.ReturnUrl are parsed out.
}
Update the status of an existing transfer:
using Bayarcash;
await bayarcash.UpdateManualBankTransferStatusAsync(
"ref_no_here",
Fpx.StatusSuccess.ToString(),
"10.00");
Portals & FPX Banks
// All portals for your account
var portals = await bayarcash.GetPortalsAsync();
// Payment channels available for a portal
var channels = await bayarcash.GetChannelsAsync("your_portal_key");
// FPX banks (for building a bank selector)
var banks = await bayarcash.FpxBanksListAsync();
Error Handling
Failed API calls throw typed exceptions, all deriving from BayarcashException:
using Bayarcash.Exceptions;
try
{
var paymentIntent = await bayarcash.CreatePaymentIntentAsync(data);
}
catch (ValidationException ex)
{
// 422 — invalid request data
var errors = ex.Errors; // IReadOnlyDictionary<string, JsonElement>
}
catch (NotFoundException)
{
// 404 — resource not found
}
catch (RateLimitException ex)
{
// 429 — too many requests
var resetAt = ex.RateLimitResetsAt; // unix timestamp or null
}
catch (FailedActionException ex)
{
// 400 — request failed
var message = ex.Message;
}
catch (BayarcashApiException ex)
{
// any other non-success status
var status = ex.StatusCode;
}
| Exception | HTTP | Meaning |
|---|---|---|
ValidationException |
422 | Invalid data. Errors holds the decoded detail. |
FailedActionException |
400 | Request failed. Message has the reason. |
NotFoundException |
404 | Resource not found. |
RateLimitException |
429 | Rate limited. RateLimitResetsAt holds the reset time. |
BayarcashApiException |
other | Unmapped failure. StatusCode / Body hold the response. |
TimeoutException |
— | Polling / retry timeout. |
Response Objects
API methods return typed response objects. Any field the API omits is null.
PaymentIntent (from CreatePaymentIntentAsync / GetPaymentIntentAsync)
paymentIntent.Url; // checkout URL to redirect the payer to
paymentIntent.Id;
paymentIntent.Status;
paymentIntent.Amount; // decimal?
paymentIntent.OrderNumber;
paymentIntent.PayerName;
paymentIntent.PayerEmail;
Transaction (from GetTransactionAsync / transaction queries)
transaction.Id;
transaction.Status; // status code — see Fpx constants
transaction.StatusDescription;
transaction.Amount; // decimal?
transaction.OrderNumber;
transaction.ExchangeReferenceNumber;
transaction.PayerName;
transaction.PayerEmail;
Dependency Injection
BayarcashClient accepts an HttpClient, so it plays well with IHttpClientFactory:
services.AddHttpClient();
services.AddSingleton(sp =>
{
var httpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient();
return new BayarcashClient("YOUR_API_TOKEN", new BayarcashOptions { ApiVersion = "v3" }, httpClient);
});
When you supply your own HttpClient, you own its lifetime; otherwise the client creates and
disposes an internal one.
Security Recommendations
- Always send a
checksumwith payment and mandate requests. - Verify every callback with the provided verification methods before acting on it.
- Store and check transaction ids to prevent duplicate processing.
- Use HTTPS for your
return_urlandcallback_url. - Keep your API token and secret key out of source control.
API Documentation
For full API details, see the Official Bayarcash API Documentation.
Support
For support questions, contact Bayarcash support or open an issue in this repository.
Changelog
See CHANGELOG.md for the version history.
License
Open-sourced software licensed under the MIT license.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Text.Json (>= 8.0.5)
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 111 | 7/22/2026 |