因為在賣迷片的朋友要跟風用 line pay 讓人付款 , 佛心幫忙弄看看 ~
有用的東東
必看影片
沙箱申請
測試訂單
HMACSHA256 算法
公式 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); 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 ) { string channelSecret = "YourSecretKey" ; 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" } }; var opt = new JsonSerializerOptions { 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); string nonce = Guid.NewGuid().ToString(); Console.WriteLine("Authorization-Nonce:" ); Console.WriteLine(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 { 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 ; } } }