Linepay

寫在前面

這次的筆記是關於Linepay的串接,有區分線上及線下的api,我們是在購物網站上使用,所以會是對線上的部分做紀錄。
串接之前,要先去申請一組sandbox 的帳號,作為測試串接使用,申請通過後會得到一組測試的帳號密碼,裡面可以找到要串接api 需要有的channel_id
channel_secret_key

先將申請連結及文件連結放上來:

申請sandbox 帳號

online api 文件

簽章方法

以下正文:

付款流程

首先先了解到,使用Linepay的整個流程:
消費者在結帳頁面填妥資料並選擇Linepay後進行結帳

呼叫Request Api

跳轉LinePay 付款頁面

使用者選擇付款方式(一卡通、Line Points、信用卡)

callback (ConfirmURL)

呼叫 Confirm Api確認付款結果

跳轉至付款完成頁

付款

接著我們會準備好付款及確認的程式碼,我是將付款及呼叫api 的程式提出來寫成一個class,
而確認的api 因為就直接寫在linepay 的異步通知裡面,共用同一個呼叫api 的方法。

  • 付款 class
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    <?php //>

    namespace your_namespace;

    class Linepay {

    public static function applyPayment($order, $member) {
    // [
    // 'url' => 'https://sandbox-api-pay.line.me',

    // 'currency' => 'TWD',

    // 'channel-id' => '你的channel-id',

    // 'channel-secret' => '你的channel-secret-key',

    // 'product-name' => '產品名稱',
    // ]
    $linepay = "config 取出 linepay 設定檔";

    $amount = "總應付金額";

    $param = [
    'amount' => $amount,
    'currency' => $linepay['currency'],
    'orderId' => "{$order['order_no']}v{$order['payment_ver']}", // 帶版本號的訂單號碼
    'packages' => [[
    'id' => "{$order['id']}",
    'amount' => $amount,
    'products' => [[
    'name' => $linepay['product-name'], //
    'quantity' => 1,
    'price' => $amount,
    ]],
    ]],
    'redirectUrls' => [
    'confirmUrl' => 'your-callback-url',
    'cancelUrl' => 'your-cancel-url',
    ],
    ];

    $response = self::request('/v3/payments/request', $linepay, $param);

    if ($response) {
    if ($response['returnCode'] === '0000') {
    $order['cashier'] = $response['info']['paymentUrl']['web']; // 網頁付款網址
    $order['cashier_type'] = 'redirect'; // 告訴前台是要跳轉
    $order['payment_request'] = json_encode($param, JSON_UNESCAPED_UNICODE); //將付款request 記錄下來
    $order['payment_response'] = json_encode($response, JSON_UNESCAPED_UNICODE); //將付款response記錄下來

    return $order;
    } else {
    return false;
    }
    }

    return null;
    }

    public static function request($api, $linepay, $param) {
    $nonce = round(microtime(true) * 1000); //唯一值,也可以產生UUID
    $data = json_encode($param, JSON_UNESCAPED_UNICODE);
    $authorization = base64_encode(hash_hmac('sha256', "{$linepay['channel-secret']}{$api}{$data}{$nonce}", $linepay['channel-secret'], true));// 官方文件上有註明簽章方式

    $headers = [
    'Content-Type: application/json',
    "X-LINE-ChannelId: {$linepay['channel-id']}",
    "X-LINE-Authorization-Nonce: {$nonce}",
    "X-LINE-Authorization: {$authorization}",
    ];

    $request = [
    'http' => [
    'method' => 'POST',
    'header' => implode("\r\n", $headers),
    'content' => $data,
    ],
    ];

    $response = @file_get_contents("{$linepay['url']}{$api}", false, stream_context_create($request)); // 呼叫api


    return json_decode($response, true);
    }

    }

準備好class 後就可以來使用了

  • execute (前台呼叫成立訂單&執行付款)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?php //>

    // ... 略過建立訂單流程
    // 如果訂單金額 > 0 呼叫Linepay 付款
    $order = call_user_func(['your_namespace\Linepay', 'applyPayment'], $order, $member);
    if ($order === false) {
    return ['error' => '連線失敗'];
    } else if ($order === null) {
    return ['error' => '串接失敗'];
    }

    $order = '資料庫更新訂單';

    return [
    'success' => true,
    'order' => $order, // 根據order的內容,前台會知道要跳轉至Linepay 付款網址
    ];
  • callback (ConfirmUrl)

Linepay 在消費者完成付款之後,會根據ConfirmUrl 帶上orderId,通知站方該筆訂單的付款狀態已變更,
我們收到這個通知的時候,要去確認此筆訂單付款狀態為何,並更新資料庫中的訂單資訊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php //>
// 我們傳送過去的訂單號碼,有多帶付款的版本號碼
$order = $this->getOrder(strstr(@$form['orderId'], 'v', true));
// 當訂單存在,且該筆訂單為未付款狀態才進行確認
if ($order && $order['status'] === 1) {
$transaction = @$form['transactionId'];

$linepay = 'linepay 設定檔';

// 確認api 必要參數
$param = [
'amount' => '總應付金額',
'currency' => $linepay['currency'],
];

// 透過寫好的class 進行呼叫
$response = Linepay::request("/v3/payments/{$transaction}/confirm", $linepay, $param);

if ($response && $response['returnCode'] === '0000' && $response['info']['orderId'] === $form['orderId']) {
// 更新訂單付款時間及狀態
$order = $this->updateOrder($order['id'], [
'pay_time' => now(),
'payment_response' => json_encode($form, JSON_UNESCAPED_UNICODE),
'payment_notice' => json_encode($response, JSON_UNESCAPED_UNICODE),
'status' => 2,
]);


return [
'success' => true,
'redirect' => $this->getOrderPath($order), // 跳轉至訂單頁面
];

}

return [
// 跳轉至錯誤頁面
];


protected function getOrder($order_no) {
return '根據訂單號碼從資料庫取出該筆訂單';
}

protected function getOrderPath($order) {
return '你的網站要顯示訂單頁面的網址';
}

protected function updateOrder($id, $data) {
return '更新資料庫的訂單資訊'
}

}

sandbox 沒有看到取消付款的功能,所以暫時先不準備取消付款的作業,

原則上只要沒有付款成功,該筆訂單的付款狀態就會維持未付款,

消費者可以在訂單查詢頁面再次付款,如果有特別設定經過多久會變成取消訂單,才會無法付款。

以上是簡單的linepay 付款串接,後續可以根據使用情境不同,再另外串接作廢、退款等等的api。