# 使用PHP SDK收款

> 安装Bayarcash PHP SDK，在API v3上创建付款、验证callback，并从你自己的PHP网站查询交易。
>
> Source: https://docs.bayarcash.com/zh/php-sdk/

Bayarcash PHP SDK让你的PHP网站创建付款、验证callback和查询交易，不用自己编写HTTP调用。本指南使用当前版本API v3。SDK在[GitHub上的README](https://github.com/bayarcash/php-sdk)列出了每个method。

如果你用Laravel，请改用[Laravel package](https://github.com/bayarcash/laravel)：它封装了这个SDK，并加入route和配置。见[选择Bayarcash SDK](/zh/sdks/)。

## 开始之前

你需要：

- PHP 7.4或更新版本（带`json` extension），以及Composer。
- 商家后台（merchant dashboard）中的Personal Access Token（PAT）、API Secret Key和一个portal key。开发期间请使用sandbox key。

## 安装SDK

在你的项目文件夹中运行：

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

这个package以前叫`webimpian/bayarcash-php-sdk`。旧的安装仍然可以用，但新的项目请使用`bayarcash/php-sdk`。

## 创建client

用你的token创建一个client，然后把它切换到API v3。SDK从v2开始，而v2已停用。

```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');
```

在发出任何request之前，先调用`setApiVersion()`和`useSandbox()`。

## 收款

要收款，先建立request并签署它，然后把你的顾客送到付款页面：

1. 用你的portal key、订单资料和URL建立request。
2. 加上用你的API Secret Key签署的checksum。
3. 创建payment intent，并把顾客重定向到它的`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;
```

主要的request field如下：

| Field | 必填 | 说明 |
|---|---|---|
| `portal_key` | 是 | 接收付款的portal。 |
| `order_number` | 是 | 你的参考编号，最多30个字符。 |
| `amount` | 是 | 最多2位小数的字符串，例如`'10.00'`。 |
| `payer_name`, `payer_email` | 是 | 顾客的姓名和电邮。 |
| `payment_channel` | 否 | 一个`Bayarcash::*` constant，或由它们组成的array。不填的话，由顾客自己选择。 |
| `payer_telephone_number` | 否 | 电子钱包和DuitNow渠道需要。 |
| `callback_url`, `return_url` | 否 | Bayarcash通知你服务器的地址，以及把顾客送回的地址。 |

checksum由`payment_channel`、`order_number`、`amount`、`payer_name`和`payer_email`生成，所以要在这些field确定后才加上。要安全地重试超时的request，把一个UUID作为第二个参数传入：`createPaymentIntent($data, $idempotencyKey)`。

## 验证callback

付款到达最终状态时，Bayarcash会把结果POST到你的`callback_url`。更新订单前先验证它：

```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);
```

在你的`return_url`页面使用`verifyReturnUrlCallbackData()`。在v3中，顾客回来时field在query string里，所以要传入`$_GET`。向顾客显示结果，但只根据callback更新订单。原因见[验证callback和checksum](/zh/callbacks-and-checksum/)。

## 查询付款和交易

这些调用读取Bayarcash记录的资料：

```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.
```

要取消顾客还未付款的payment intent，调用`$bayarcash->cancelPaymentIntent('payment_intent_id')`。

## 处理错误

调用失败时，会抛出`Bayarcash\Exceptions`中的exception：

| Exception | HTTP状态 | 怎么做 |
|---|---|---|
| `ValidationException` | 422 | 修正request。`$e->errors()`会列出相关field。 |
| `FailedActionException` | 400 | 阅读`$e->getMessage()`了解原因。 |
| `NotFoundException` | 404 | 检查你查询的ID。 |
| `RateLimitExceededException` | 429 | 稍等。`$e->rateLimitResetsAt`里有重设时间。 |

## 小贴士

- **先在sandbox测试整个流程**。创建一笔付款，然后付款。换成正式key之前，确认你的callback会把订单标为已付款。
- **不要把token和API Secret Key写在代码里**。像上面的示例一样，从环境变量或框架的secret store读取。
- **使用渠道constant，不要用数字。**`Bayarcash::FPX`比`1`更容易看懂，README列出了每个渠道constant。

## 常见问题

### 更新SDK后，我收到：Class "Webimpian\BayarcashSdk\Bayarcash" not found。改了什么？

SDK第3版把namespace改名为Bayarcash。把你的import改为use Bayarcash\Bayarcash;（以及Bayarcash\Fpx、Bayarcash\FpxDirectDebit）。新项目也应该安装bayarcash/php-sdk，而不是旧的webimpian/bayarcash-php-sdk package。

### 我收到“The getTransactionByOrderNumber method is only available for API version v3.”。为什么？

SDK一开始使用API v2。创建client后，在发出任何request之前，马上调用$bayarcash->setApiVersion('v3')。

### createPaymentIntent抛出ValidationException。怎样查看哪里出错？

对这个exception调用$e->errors()。它会列出API拒绝的每个field，例如超出允许范围的金额，或超过30个字符的订单号码。
