# Accept payments with the PHP SDK

> Install the Bayarcash PHP SDK, create a payment on API v3, verify the callback and look up transactions from your own PHP site.
>
> Source: https://docs.bayarcash.com/php-sdk/

The Bayarcash PHP SDK lets your PHP site create payments, verify callbacks and look up transactions without writing the HTTP calls yourself. This guide uses API v3, the current version. The SDK's [README on GitHub](https://github.com/bayarcash/php-sdk) has every method.

On Laravel, use the [Laravel package](https://github.com/bayarcash/laravel) instead: it wraps this SDK and adds routes and configuration. See [Choose a Bayarcash SDK](/sdks/).

## Before you start

You need:

- PHP 7.4 or newer with the `json` extension, and Composer.
- Your Personal Access Token (PAT), API Secret Key and a portal key from the merchant dashboard. Use sandbox keys while you build.

## Install the SDK

Run this in your project folder:

```bash
composer require bayarcash/php-sdk
```

The package used to be called `webimpian/bayarcash-php-sdk`. Old installations still work, but use `bayarcash/php-sdk` for anything new.

## Create the client

Create one client with your token, then switch it to API v3. The SDK starts on v2, which is deprecated.

```php
use Bayarcash\Bayarcash;

$bayarcash = new Bayarcash(getenv('BAYARCASH_PAT'));
$bayarcash->setApiVersion('v3');
$bayarcash->useSandbox(); // remove this line in production

$apiSecretKey = getenv('BAYARCASH_API_SECRET_KEY');
```

Call `setApiVersion()` and `useSandbox()` before you make any request.

## Take a payment

To take a payment, build the request, sign it, and send your customer to the payment page:

1. Build the request with your portal key, order details and URLs.
2. Add a checksum signed with your API Secret Key.
3. Create the payment intent and redirect the customer to its `url`.

```php
$data = [
    'portal_key'             => 'your_portal_key',
    'payment_channel'        => Bayarcash::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',
];

$data['checksum'] = $bayarcash->createPaymentIntentChecksumValue($apiSecretKey, $data);

$paymentIntent = $bayarcash->createPaymentIntent($data);

header('Location: ' . $paymentIntent->url);
exit;
```

The main request fields are:

| Field | Required | Notes |
|---|---|---|
| `portal_key` | Yes | The portal that receives the payment. |
| `order_number` | Yes | Your reference, up to 30 characters. |
| `amount` | Yes | A string with up to 2 decimals, such as `'10.00'`. |
| `payer_name`, `payer_email` | Yes | The customer's name and email. |
| `payment_channel` | No | One `Bayarcash::*` constant or an array of them. Leave it out to let the customer choose. |
| `payer_telephone_number` | No | Needed for e-wallet and DuitNow channels. |
| `callback_url`, `return_url` | No | Where Bayarcash notifies your server, and where it sends the customer back. |

The checksum is built from `payment_channel`, `order_number`, `amount`, `payer_name` and `payer_email`, so add it after those fields are final. To retry a timed-out request safely, pass a UUID as a second argument: `createPaymentIntent($data, $idempotencyKey)`.

## Verify the callback

Bayarcash posts the result to your `callback_url` when the payment reaches a final state. Verify it before you update the order:

```php
use Bayarcash\Fpx;

$callbackData = $_POST;

if (! $bayarcash->verifyTransactionCallbackData($callbackData, $apiSecretKey)) {
    http_response_code(403);
    exit;
}

if ((int) $callbackData['status'] === Fpx::STATUS_SUCCESS) {
    // Mark the order with $callbackData['order_number'] as paid.
}

http_response_code(200);
```

Use `verifyReturnUrlCallbackData()` on your `return_url` page. On v3 the customer comes back with the fields in the query string, so pass `$_GET`. Show the result to the customer, but update the order from the callback only. See [Verify callbacks and checksums](/callbacks-and-checksum/) for why.

## Look up payments and transactions

These calls read what Bayarcash has recorded:

```php
$intent      = $bayarcash->getPaymentIntent('payment_intent_id');
$transaction = $bayarcash->getTransaction('transaction_id');

$byOrder = $bayarcash->getTransactionByOrderNumber('INV-1001');

$result = $bayarcash->getAllTransactions([
    'status'          => '3',
    'payment_channel' => Bayarcash::FPX,
]);
// $result['data'] holds the transactions, $result['meta'] the pagination.
```

To cancel a payment intent the customer has not paid, call `$bayarcash->cancelPaymentIntent('payment_intent_id')`.

## Handle errors

Failed calls throw exceptions from `Bayarcash\Exceptions`:

| Exception | HTTP status | What to do |
|---|---|---|
| `ValidationException` | 422 | Fix the request. `$e->errors()` lists the fields. |
| `FailedActionException` | 400 | Read `$e->getMessage()` for the reason. |
| `NotFoundException` | 404 | Check the ID you asked for. |
| `RateLimitExceededException` | 429 | Wait. `$e->rateLimitResetsAt` has the reset time. |

## Tips

- **Test the whole flow in the sandbox first.** Create a payment, pay it, and check that your callback marks the order paid before you switch to production keys.
- **Keep the token and API Secret Key out of your code.** Load them from environment variables or your framework's secret store, as in the examples above.
- **Use the channel constants instead of numbers.** `Bayarcash::FPX` reads better than `1`, and the README lists every channel constant.

## Common issues

### After updating the SDK I get: Class "Webimpian\BayarcashSdk\Bayarcash" not found. What changed?

Version 3 of the SDK renamed its namespace to Bayarcash. Change your imports to use Bayarcash\Bayarcash; (and Bayarcash\Fpx, Bayarcash\FpxDirectDebit). New projects should also install bayarcash/php-sdk instead of the old webimpian/bayarcash-php-sdk package.

### I get "The getTransactionByOrderNumber method is only available for API version v3." Why?

The SDK starts on API v2. Call $bayarcash->setApiVersion('v3') right after you create the client, before any request.

### createPaymentIntent throws a ValidationException. How do I see what is wrong?

Call $e->errors() on the exception. It lists each field the API rejected, for example an amount outside the allowed range or an order number longer than 30 characters.
