0%

.net core LINE PAY API v3 筆記

 

因為在賣迷片的朋友要跟風用 line pay 讓人付款 , 佛心幫忙弄看看 ~

有用的東東

  1. 必看影片

  2. 沙箱申請

  3. 測試訂單

  4. HMACSHA256 算法

  5. 公式 Signature = Base64(HMAC-SHA256(Your ChannelSecret, (Your ChannelSecret + URI + RequestBody + nonce)))

準備類別

先利用 json2csharp 將 json 物件轉換為 c# 類別,這個實際情況類別會更複雜,在此先能跑而已
測試 json 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"amount":4000,
"currency":"TWD",
"orderId":"order504ac11a-1888-4410-89b2-75382fef61b3",
"packages":
[
{
"id":"20191011I001","amount":4000,"name":"測試",
"products":[
{
"name":"測試商品","quantity":2,"price":2000
}
]
}
],
"redirectUrls":{"confirmUrl":"https://6ddcf789.ngrok.io/confitmUrl",
"cancelUrl":"https://6ddcf789.ngrok.io/cancelUrl"
}
}

C# 類別

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
public class Product
{
public string name { get; set; }
public int quantity { get; set; }
public int price { get; set; }
}

public class Package
{
public string id { get; set; }
public int amount { get; set; }
public string name { get; set; }
public List<Product> products { get; set; }
}

public class RedirectUrls
{
public string confirmUrl { get; set; }
public string cancelUrl { get; set; }
}

public class LineForm
{
public int amount { get; set; }
public string currency { get; set; }
public string orderId { get; set; }
public List<Package> packages { get; set; }
public RedirectUrls redirectUrls { get; set; }
}

撰寫核心加密算法

特別注意這邊很重要因為我們使用中文,稍早我們撰寫訂單時故意用中文,如果你訂單用英文不需要 UTF8Encoding 也可以跑,但是防止錯誤編碼使用 UTF8Encoding 比較直接了當。 此外要回傳 Base64 字串 , 參考

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string LinePayHMACSHA256(string key, string message)
{
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
byte[] keyByte = encoding.GetBytes(key);

HMACSHA256 hmacsha256 = new HMACSHA256(keyByte);

byte[] messageBytes = encoding.GetBytes(message);
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);

//注意他原本的公式是直接轉為string
return Convert.ToBase64String(hashmessage);
}

這邊要參考先前的公式進行組合
string channelSecret = "你的LINE後台金鑰";

這個 nonce 要保留下來這樣 postman 執行時也需要這串
string nonce = Guid.NewGuid().ToString();

可能會長這樣 3ac44b8e-50bc-463e-b48b-bc238f3bb935

這邊是呼要 request 所以要這樣寫,如果有其他操作要自己替換,特別注意前面不要加 http://xxxxx 很單純只要寫 Uri 即可,非完全的網址。
string requestUri = " /v3/payments/request ";

將 JSON 物件轉換為 JSON 字串,實際在 MVC 的話一進來就是 JSON 物件只要轉換為字串即可。
另外我這邊有用 ngrok 進行穿透 , 方便我測試

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
LineForm json = new LineForm { 
amount = 4000,
currency = "TWD",
orderId = "order504ac11a-1888-4410-89b2-75382fef61b3",
packages = new List<Package> {
new Package{
id = "20191011I001",
amount = 4000,
name = "測試",
products = new List<Product>{
new Product{
name = "測試商品",
quantity = 2,
price = 2000
}
}
}
},
redirectUrls = new RedirectUrls
{
confirmUrl = "https://6ddcf789.ngrok.io/confitmUrl",
cancelUrl = "https://6ddcf789.ngrok.io/cancelUrl"
}
};
string form = JsonConvert.SerializeObject(json);

參考官方公式組合出來
Signature = Base64(HMAC-SHA256(Your ChannelSecret, (Your ChannelSecret + URI + RequestBody + nonce)))

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
LineForm json = new LineForm { 
amount = 4000,
currency = "TWD",
orderId = "order504ac11a-1888-4410-89b2-75382fef61b3",
packages = new List<Package> {
new Package{
id = "20191011I001",
amount = 4000,
name = "測試",
products = new List<Product>{
new Product{
name = "測試商品",
quantity = 2,
price = 2000
}
}
}
},
redirectUrls = new RedirectUrls
{
confirmUrl = "https://6ddcf789.ngrok.io/confitmUrl",
cancelUrl = "https://6ddcf789.ngrok.io/cancelUrl"
}
};
string form = JsonConvert.SerializeObject(json);
string result = LinePayHMACSHA256(ChannelSecret, ChannelSecret + requestUri + form + nonce);

使用 postman 測試

先選擇 POST
並且輸入以下網址 https://sandbox-api-pay.line.me/v3/payments/request
接著在 Header 上填入對應的值
Content-Type
application/json

X-LINE-ChannelId
你的 LINE PAY 後台商店編號

X-LINE-Authorization-Nonce
先前 c# 這串保留下來的值
string nonce = Guid.NewGuid().ToString();
3ac44b8e-50bc-463e-b48b-bc238f3bb935

X-LINE-Authorization
使用公式組合出來的 base64 字串
string result = LinePayHMACSHA256(ChannelSecret, ChannelSecret + requestUri + form + nonce);
iVY2vGxJNJ+ivjxNfa/Rj1QC4SKblidDs0J3NrKNgXU=

接著填寫 Body
Body 只要填寫訂單的 JSON 物件即可 body => raw => json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"amount":4000,
"currency":"TWD",
"orderId":"order504ac11a-1888-4410-89b2-75382fef61b3",
"packages":[
{
"id":"20191011I001","amount":4000,"name":"測試",
"products":[{"name":"測試商品","quantity":2,"price":2000}]
}
],
"redirectUrls":
{
"confirmUrl":"https://6ddcf789.ngrok.io/confitmUrl",
"cancelUrl":"https://6ddcf789.ngrok.io/cancelUrl"
}
}

成功以後會出現如下 JSON

1
2
3
4
5
6
7
8
9
10
11
12
{
"returnCode": "0000",
"returnMessage": "Success.",
"info": {
"paymentUrl": {
"web": "https://sandbox-web-pay.line.me/web/payment/wait?transactionReserveId=S3JROFFsQ2g1Ukd3YzNRTUlQRUJFcFZ2aDdHbEx5T2pVNjh0ZFBPQ3NRNWRhbU9PUHl6dHJ3WklWUHBvTUZ4ag",
"app": "line://pay/payment/S3JROFFsQ2g1Ukd3YzNRTUlQRUJFcFZ2aDdHbEx5T2pVNjh0ZFBPQ3NRNWRhbU9PUHl6dHJ3WklWUHBvTUZ4ag"
},
"transactionId": 2020022600460257310,
"paymentAccessToken": "877307055245"
}
}

如果出現 1106 標頭 (Header) 資訊錯誤,則表示先前的 GUID 或是密鑰有寫錯,導致加密錯誤
注意當我們取得 paymentUrl 以後需要使用手機開啟,最好不要用電腦去點開,
否則之後可能會出現錯誤,所以在撰寫時最好申請兩組沙箱來使用,防止錯誤
(至少我當時在 try 的時候有發生過不明錯誤)

中文亂碼設定

.net core 預設會把中文進行 encoding 所以輸出時如果看到類似這種 \u54C1 output
若真的需要輸出中文方便 log 或 trace 可以使用以下設定

1
2
3
4
5
6
var pot = new JsonSerializerOptions{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
//關鍵點可以輸出中文
Encoder = System.Text.Encoding.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string order = JsonSerializer.Serialize<LineForm>(json,opt);

full example

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ConsoleCoreLinePay
{
class Program
{
static void Main(string[] args)
{
//LINE 後台的 Channel Secret Key
string channelSecret = "YourSecretKey";

//LINE PAY 的 Request 網址
string requestUri = "/v3/payments/request";

LineForm json = new LineForm
{
Amount = 4000,
Currency = "TWD",
OrderId = "order504ac11a-1888-4410-89b2-75382fef61b3",
Packages = new List<Package> {
new Package{
Id = "20191011I001",
Amount = 4000,
Name = "測試",
Products = new List<Product>{
new Product{
Name = "測試商品",
Quantity = 2,
Price = 2000
}
}
}
},
RedirectUrls = new RedirectUrls
{
ConfirmUrl = "https://6ddcf789.ngrok.io/confitmUrl",
CancelUrl = "https://6ddcf789.ngrok.io/cancelUrl"
}
};

//舊版會讓中文字以中文的方式顯示在 console 上
//.net core 會把 utf8 字元改成如 python 那種格式 \u6E2C\u8A66\u5546\u54C1
var opt = new JsonSerializerOptions
{
//設定Json屬性為駝峰小寫
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
//設定這樣就會顯示正常的中文
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string order = JsonSerializer.Serialize<LineForm>(json , opt);


Console.WriteLine("測試");
Console.WriteLine("order json :");
Console.WriteLine(order);

//一次性的Guid
//這邊如果需要測試記得保存一下
string nonce = Guid.NewGuid().ToString();
Console.WriteLine("Authorization-Nonce:");
Console.WriteLine(nonce);

//理論上的公式
//特別注意toJson的地方範例是用NodeJs將JSON轉為JSON string
//LinePayHMACSHA256(ChannelSecret, ChannelSecret + requestUri + toJson(form) + nonce);

string result = LinePayHMACSHA256(
channelSecret,
channelSecret + requestUri + order + nonce);
Console.WriteLine("Authorization:");
Console.WriteLine(result);
}

public static string LinePayHMACSHA256(string key, string data)
{
System.Text.Encoding encoding = new System.Text.UTF8Encoding();
byte[] keyByte = encoding.GetBytes(key);

HMACSHA256 hmacsha256 = new HMACSHA256(keyByte);

byte[] messageBytes = encoding.GetBytes(data);
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);

return Convert.ToBase64String(hashmessage);

}
}

public class Product
{
[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("quantity")]
public int Quantity { get; set; }

[JsonPropertyName("price")]
public int Price { get; set; }
}

public class Package
{
[JsonPropertyName("id")]
public string Id { get; set; }

[JsonPropertyName("amount")]
public int Amount { get; set; }

[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("products")]
public List<Product> Products { get; set; }
}

public class RedirectUrls
{
[JsonPropertyName("confirmUrl")]
public string ConfirmUrl { get; set; }

[JsonPropertyName("cancelUrl")]
public string CancelUrl { get; set; }
}

public class LineForm
{
//[JsonPropertyName("amount")]
public int Amount { get; set; }

//[JsonPropertyName("currency")]
public string Currency { get; set; }

//[JsonPropertyName("orderId")]
public string OrderId { get; set; }

//[JsonPropertyName("packages")]
public List<Package> Packages { get; set; }

//[JsonPropertyName("redirectUrls")]
public RedirectUrls RedirectUrls { get; set; }
}
}
關閉