怎麼串接綠界金流?以 Angular & .NET 實作

文章目錄

綠界金流串接付款成功 如何串接綠界金流?以 Angular 、 .NET 實作網站結帳付款功能。

綠界金流串接流程

綠界金流串接Sequence Diagram

  1. 使用者按下結帳按鈕。
  2. Angular將使用者結帳商品資訊傳送至.NET。
  3. .Net以結帳商品資訊產生請求綠界付款頁面所需的參數回傳給Angular。
  4. Angular接收參數後,向綠界發送請求,以產生綠界付款訂單。
  5. 綠界顯示付款頁面給使用者。
  6. 使用者付款完成。
  7. 綠界發送付款結果通知給.NET。
  8. .NET接收通知,更新資料庫,導回指定頁面。

一、準備綠界商店串接資料

在 appsettings.json 中配置綠界商店資訊。

"ECPay": {
    "MerchantID": "3002607",
    "HashKey": "pwFHCqoQZGmho4w6",
    "HashIV": "EkRm7iFT261dpevs"
}

依賴注入綠界商店資訊。

private readonly string _baseUrl;
private readonly string _hashKey;
private readonly string _hashIV;
private readonly string _merchantId;

private readonly ICartService _cartService;
public ECPayService(IConfiguration config)
{
    _baseUrl = config["urls"] ?? throw new Exception("urls is null.");
    _hashKey = config["ECPay:HashKey"] ?? throw new Exception("ECPay:HashKey is null.");
    _hashIV = config["ECPay:HashIV"] ?? throw new Exception("ECPay:HashIV is null.");
    _merchantId = config["ECPay:MerchantID"] ?? throw new Exception("ECPay:MerchantID is null.");
}

二、建立綠界檢查碼機制

private string GetCheckMacValue(Dictionary<string, string?> dix)
{
    var param = dix.Keys.OrderBy(x => x)
        .Where(key => dix[key] != null)
        .Select(key => key + "=" + dix[key]).ToList();
    var checkValue = string.Join("&", param);
    checkValue = $"HashKey={_hashKey}&{checkValue}&HashIV={_hashIV}";
    checkValue = HttpUtility.UrlEncode(checkValue).ToLower();
    checkValue = GetSHA256(checkValue);
    return checkValue.ToUpper();
}

private static string GetSHA256(string value)
{
    var result = new StringBuilder();
    using SHA256 sha256 = SHA256.Create();
    byte[] inputBytes = Encoding.UTF8.GetBytes(value);
    byte[] hashBytes = sha256.ComputeHash(inputBytes);
    for (int i = 0; i < hashBytes.Length; i++)
    {
        result.Append(hashBytes[i].ToString("X2"));
    }
    return result.ToString();
}

三、產生綠界付款頁面所需參數

將購物車產品資訊傳送到後端,以後端產生付款頁面所需參數。

public Dictionary<string, string> GetTradeOrder(ECPayTradeData data)
{
    var order = new Dictionary<string, string>
    {
        { "MerchantID",  _merchantId },
        { "MerchantTradeNo",  data.MerchantTradeNo },
        { "MerchantTradeDate",  DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") },
        { "PaymentType",  "aio" },
        { "TotalAmount",  $"{data.TotalAmount}" },
        { "TradeDesc",  data.TradeDesc },
        { "ItemName", data.ItemName },
        { "ReturnURL",  $"{_baseUrl}/api/ECPay" },
        { "OrderResultURL", $"{_baseUrl}/api/ECPay/Redirect" },
        { "ChoosePayment",  "ALL" },
        { "IgnorePayment",  "TWQR#CVS#BARCODE#ApplePay#BNPL" },
        { "EncryptType", "1" },
        { "CustomField1",  data.CartProductIds }
    };

    order["CheckMacValue"] = GetCheckMacValue(order!);

    return order;
}
  • ChoosePayment:指定付款方式。

    綠界提供的付款方式:

    • Credit:信用卡及銀聯卡(需申請開通)
    • TWQR :歐付寶TWQR行動支付(需申請開通)
    • WebATM:網路ATM
    • ATM:自動櫃員機
    • CVS:超商代碼
    • BARCODE:超商條碼
    • ApplePay: Apple Pay(僅支援手機支付)
    • BNPL:裕富無卡分期(需申請開通)
    • ALL:不指定付款方式,由綠界顯示付款方式選擇頁面。
  • IgnorePayment:隱藏不需要的付款方式(以#分隔)。

    • 指定2個以上的付款方式,需將ChoosePayment設定為ALL,再以IgnorePayment隱藏不需要的付款方式。
  • MerchantTradeNo:自定義不可重複的訂單編號

  • ReturnURL:指定POST端點接收綠界回傳的付款結果通知。

  • OrderResultURL:指定POST端點以導向特定前端畫面。

四、產生綠界付款頁面

前端接收來自後端產生的綠界付款頁面參數,以 form 表單 post 方法發送請求至綠界,以產生綠界付款頁面。

<form #payForm method="post" action="https://payment-stage.ecpay.com.tw/Cashier/AioCheckOut/V5">
    @for (item of data | keyvalue; let last = $last; track $index) {
        <input type="hidden" [name]="formatKey(item.key)" [value]="item.value">
        @if (last) {
            {{ payForm.submit() }}
        }
    }
</form>
@Component({
  selector: 'pay',
  standalone: true,
  imports: [KeyValuePipe],
  templateUrl: './pay.component.html',
  styleUrl: './pay.component.css'
})
export class PayComponent {
  @Input() data?: {};

  formatKey(str: string): string {
    if (str.length > 0) {
      return str.charAt(0).toUpperCase() + str.slice(1);
    }
    return str;
  }
}

五、接收付款通知

綠界會將付款結果以POST方式回傳到ReturnURL

建立後端端點,以接收付款通知。驗證檢查碼正確,確認訂單付款完成,才新增訂單。

[HttpPost]
public async Task<string> OnPayResult([FromForm] IFormCollection result)
{
    try
    {
        // 驗證檢查碼
        var data = _service.HandlePayResult(result);

        var order = new Order()
        {
            MerchantTradeNo = data["MerchantTradeNo"]!,
            TradeNo = data["TradeNo"]!,
            TradeAmt = int.Parse(data["TradeAmt"]!),
            TradeStatus = data["RtnCode"]!,
            PaymentDate = data["PaymentDate"]!,
            PaymentType = data["PaymentType"]!
        };

        // 付款完成才將訂單寫入資料庫
        if(order.TradeStatus == "1")
        {
            var cartProductIds = data["CustomField1"]!.Split(",").ToList();

            await _orderService.CreateOrderAsync(order, cartProductIds);
        }
    }
    catch (Exception ex)
    {
        Log.Error(ex.Message);
    }
    return "1|OK";
}

六、付款完成自動導回指定前端畫面

綠界會將付款結果以POST方式回傳到OrderResultURL

建立後端端點接收付款結果,以Redirect導向至特定URL。

[HttpPost("Redirect")]
public IActionResult OrderResultURL([FromForm] IFormCollection result)
{
    try
    {
        return Redirect($"http://localhost:4200/order/{result["MerchantTradeNo"]}");
    }
    catch(Exception ex)
    {
        Log.Error(ex.Message);
        return Redirect($"http://localhost:4200/cart");
    }
}

七、測試金流

綠界一般信用卡

  • 測試卡號 : 4311-9522-2222-2222
  • 安全碼 : 222

進入綠界測試用的特店管理後台以「模擬付款」按鈕測試付款通知

  • 特店後台登入帳號:stagetest3
  • 特店後台登入密碼:test1234
  • 特店統一編號:00000000

附錄、建立對外臨時網址

  1. 下載ngrok

  2. 註冊並登入ngrok,取得authtoken

  3. 執行ngrok.exe

  4. 連結帳戶。

    配置 ngrok ,設定TOKEN以連結帳戶。

    ngrok config add-authtoken <TOKEN>
    

    %HOMEPATH%\AppData\Local\ngrok\ngrok.yml檔案中可見以下配置。

    version: "2"
    authtoken: <TOKEN>
    
  5. 對外開放內部網址。

    對外開放後端,輸入port以建立對外臨時網址。

    ngrok http <port>
    

    ngrok對外開放內部網址

參考資料