Bayarcash 1.0.0

dotnet add package Bayarcash --version 1.0.0
                    
NuGet\Install-Package Bayarcash -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bayarcash" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bayarcash" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Bayarcash" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bayarcash --version 1.0.0
                    
#r "nuget: Bayarcash, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bayarcash@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bayarcash&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Bayarcash&version=1.0.0
                    
Install as a Cake Tool

Bayarcash Payment Gateway .NET SDK

NuGet NuGet Downloads License

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

  • .NET targeting netstandard2.0 or 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, and System.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

  1. Always send a checksum with payment and mandate requests.
  2. Verify every callback with the provided verification methods before acting on it.
  3. Store and check transaction ids to prevent duplicate processing.
  4. Use HTTPS for your return_url and callback_url.
  5. 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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