綠界ECPAY平台金流串接實作

閱讀時間約 45 分鐘

※ 安裝ECPAY SDK(軟體開發工具包):

載點:https://github.com/ECPay/ECPayAIO_Node.js

方法:

1.把code dowonload下來。

raw-image


在專案中下載ECPAY SDK代碼的原因如下:

  • 參考學習:開發時,可以直接參考 SDK 內的內容。
  • 錯誤排查:SDK 內的資料夾和 JavaScript 內容,可以在開發過程中通過 console.log 方法來檢查,幫助我們發現參數中的錯誤。

操作流程如下:

  • 下載ECPayAIO_Node.js-master。
  • 將ECPayAIO_Node.js-master解壓縮。
  • ECPayAIO_Node.js-master資料夾移動到專案中的server資料夾裡面。
  • 在src資料夾中新增adapters資料夾adapters功能是將 API 包裝成一個或多個函數,方便在應用程式中使用。
  • adapters資料夾中新增ecpay資料夾新增index.ts撰寫關於ecpay內容。
  • 接著把ECPAY_Payment_node_js放進ecpay資料夾中其餘關於ECPayAIO_Node.js-master的檔案都刪除。

2.使用npm install。

raw-image

※ 設計ECPAY 接口:

●定義IECPayAdapter接口:ecpay → index.js

export interface IECPayAdapter {
createCVS()
}

程式碼解說:

  • export 關鍵字讓 IECPayAdapter 接口可以在其他模組中被 import,進而確保不同的類別可以依據這個接口的定義來實現 createCVS 方法。IECPayAdapter 是用來適配 ECPay 的支付功能。

●createCVS()要搭配的參數查找步驟:

  • 打開 aio_check_out_cvs.js 文件
    • 打開專案目錄中的 aio_check_out_cvs.js 文件。
  • 查找 createCVS() 函數
    • 在文件中查找 createCVS() 函數定義和使用。通常可以使用編輯器的搜尋功能(如 Ctrl + F 或 Cmd + F)快速定位。
  • 檢查 createCVS() 函數的參數
    • 檢查這個函數的參數和相關的邏輯,了解它需要哪些資料來創建 CSV。
raw-image


raw-image


程式碼解說:

CVS_INFO 接口

定義了有關 CVS(便利商店)相關資訊的結構,包括商店過期日期、描述和支付訊息網址等。

interface CVS_INFO {
StoreExpireDate: string; // 商店過期日期
Desc_1: string; // 描述1
Desc_2: string; // 描述2
Desc_3: string; // 描述3
Desc_4: string; // 描述4
PaymentInfoURL: string; // 支付訊息的網址
}

CVS_PARAMS 接口

定義了與 CVS 交易相關的參數,包括商戶交易號、交易日期、總金額、交易描述、商品名稱和回傳網址等。

interface CVS_PARAMS {
MerchantTradeNo: string; // 商戶交易號
MerchantTradeDate: string; // 商戶交易日期
TotalAmount: string; // 總金額
TradeDesc: string; // 交易描述
ItemName: string; // 商品名稱
ReturnURL: string; // 回傳網址
}

CreateBillParams 接口

interface CreateBillParams {
cvsInfo?: CVS_INFO; // CVS 信息
cvsPrams: CVS_PARAMS; // CVS 參數
inv_param?: {}; // 可選的發票參數
client_redirect_url?: string; // 可選的客戶重定向網址
}
  • 定義了用於創建帳單所需的參數結構,包含 CVS_INFOCVS_PARAMS 兩個主要部分,以及可選的發票參數和客戶重定向網址。
  • cvsInfo 屬性後加上問號,表示這個屬性是可選的(optional)。

IECPayAdapter 接口

定義了一個用於適配 ECPay 的接口,包括一個名為 createCVS 的方法。這個方法接受 CreateBullParams 類型的參數,並返回一個字串。

export interface IECPayAdapter {
createCVS(createBillParams: CreateBillParams): string; // 用於創建 CVS 的方法
}


●讓ECPayAdapter使用 ECPay 提供的 API 來執行支付相關的操作。

export class ECPayAdapter implements IECPayAdapter {
private ecpayInstance;
constructor(options: IECPayAdapterOptions = defaultOptions) {
this.ecpayInstance = new ECPAY(options);
  }
}

程式碼解說:

1.導入 ECPay 模組

import ECPAY from './ECPAY_Payment_node_js';

ECPAY_Payment_node_js 文件中的內容作為模組導入,並命名為 ECPAY。這個模組應該包含了 ECPay 的支付功能實現,例如創建 CVS、查詢支付狀態等。

※ 參考資料來源ECPAY_Payment_node_js/example/aio_check_out_cvs.js

raw-image

2.export class ECPayAdapter implements IECPayAdapter

  • ECPayAdapter 類別實現了 IECPayAdapter 接口,這表示它必須包含 IECPayAdapter 接口中定義的createCVS 方法。

3.private ecpayInstance;

這行程式碼宣告了一個私有變數 ecpayInstance,用來儲存 ECPay 的實例。private 關鍵字表示這個變數只能在 ECPayAdapter 類別內部使用,外部無法直接存取或修改。

4.constructor(options: IECPayAdapterOptions = defaultOptions)

  • 構造函數:這是 ECPayAdapter 類別的構造函數,用於在創建類別實例時進行初始化。
  • options 參數:這個參數的類型是 IECPayAdapterOptions,用來接收初始化配置選項。
  • 默認值 defaultOptions:options 參數設定為 defaultOptions 作為默認值,表示如果沒有傳遞具體的 options,則使用 defaultOptions。
  • 5.this.ecpayInstance = new ECPAY(options);
    • 這行程式碼在構造函數內執行,創建一個新的 ECPAY 實例並賦值給 this.ecpayInstance。這樣,ECPayAdapter 類別就可以使用 this.ecpayInstance 來調用 ECPay 的支付功能。
    • options 參數通常是一個配置物件,用來設置 ECPay 的一些初始化選項,例如商戶 ID、金鑰、API URL 等。

●定義optionsc後再依據 IECPayAdapterOptions 創建的一個具體變數或物件。

※ 參考資料來源ECPAY_Payment_node_js/conf/config-example.js

raw-image


interface IECPayAdapterOptions {
OperationMode: "Test" | "Production", //Test or Production
MercProfile: {
MerchantID: string;
HashKey: string;
HashIV: string;
},
"IgnorePayment": [];
"IsProjectContractor": boolean;
}

程式碼解說:

  • OperationMode
    • 類型是 "Test" | "Production",表示運行模式可以是 "Test"(測試模式)或 "Production"(生產模式)。這個屬性用來設定 ECPay 是在測試環境還是生產環境中運行。
  • MercProfile
    • 這是一個物件,包含三個字串屬性,用於設定商戶的基本資料。
      • MerchantID:商戶 ID,用於標識商戶身份。HashKey:哈希金鑰,用於加密資料。HashIV:哈希初始化向量,用於加密資料。
  • IgnorePayment
    • 這是一個空的陣列,表示要忽略的支付方式。這個屬性可以用來設定不需要支持的支付方式。
  • IsProjectContractor
    • 類型是 boolean,表示是否是項目承包商。這個屬性用來標識商戶是否以項目承包商的身份運行。

● 設定defaultOptions

//定義一組預設的配置選項
const defaultOptions: IECPayAdapterOptions
= {}

程式碼解說:

  • 設置預設值:當用戶在初始化某個功能或類別時沒有提供具體的選項,defaultOptions 中定義的預設值將會被使用,確保系統有合理的初始設置。
  • 類型IECPayAdapterOptions意味著它必須符合這個接口中定義的結構和屬性。

● 設定defaultOptions內容:定義一組預設的配置選項

const defaultOptions: IECPayAdapterOptions = {
OperationMode: "Test", //Test or Production
MercProfile: {
MerchantID: "2000132",
HashKey: "5294y06JbISpM5x9",
HashIV: "v77hoKGq4kWxNNIS"
},
"IgnorePayment": [
// "Credit",
// "WebATM",
// "ATM",
// "CVS",
// "BARCODE",
// "AndroidPay"
],
"IsProjectContractor": false
}

程式碼解說:

※ 複製資料來源ECPAY_Payment_node_js/conf/config-example.js

raw-image



● 查看HashKeyHashIV:

進入綠界科技的開發者測試後台:

raw-image

登入後進入系統開發管理:

raw-image

確認程式碼是否一樣:

raw-image


● 設定create cvs

createCVS = (createParams: CreateBillParams) => {

const { cvsInfo, cvsPrams, inv_param, client_redirect_url } = createParams;

}

程式碼解說:

createCVS = (createParams: CreateBillParams) => { ... }

  • 這是一個箭頭函數,用於處理 createCVS 方法的邏輯。
  • createParams 是函數的參數,類型為 CreateBillParams。

const { cvsInfo, cvsPrams, inv_param, client_redirect_url } = createParams;

  • 這行程式碼使用了解構賦值語法,從 createParams 參數中提取出 cvsInfocvsPramsinv_paramclient_redirect_url 屬性。
  • 這樣做的目的是為了方便在函數內部直接使用這些變數,而不必每次都通過 createParams 物件來訪問。

● 定義了一個cvsInfo 的物件:

 cvsInfo = {
StoreExpireDate: '',
Desc_1: '',
Desc_2: '',
Desc_3: '',
Desc_4: '',
PaymentInfoURL: ''
},

程式碼解說:

※ 複製資料來源ECPAY_Payment_node_js/conf/config-example.js

raw-image
  • StoreExpireDate
    • 描述:這個屬性通常用於指定便利商店的訂單過期日期。
    • 初始值:空字串。
  • Desc_1, Desc_2, Desc_3, Desc_4
    • 描述:這些屬性用於存儲額外的描述資訊,通常用於提供關於訂單或支付的詳細描述。
    • 初始值:空字串。
  • PaymentInfoURL
    • 描述:這個屬性用於指定一個 URL,該 URL 包含支付資訊,可能會將用戶重定向到相關的支付信息頁面。
    • 初始值:空字串

● 從 createParams 參數中提取 cvsPrams 屬性,並給 inv_param 和 client_redirect_url 設置默認值:

const {

cvsPrams,

inv_param = {},

client_redirect_url = ''

} = createParams;

程式碼解說:

  • cvsPrams
    • 直接從 createParams 中解構出來。
    • 這個變數用來存儲與 CVS(便利商店)相關的參數信息。
  • inv_param = {}
    • 使用解構賦值的語法,並設置默認值為空物件 {}。
    • 這個變數用來存儲發票相關的參數。如果 createParams 中沒有 inv_param 屬性,則使用默認的空物件。
  • client_redirect_url = ''
    • 使用解構賦值的語法,並設置默認值為空字串 ''。
    • 這個變數用來存儲客戶重定向的 URL。如果 createParams 中沒有 client_redirect_url 屬性,則使用默認的空字串。

● 生成的 HTML 表單輸出到控制台:

※ 複製資料來源ECPAY_Payment_node_js/conf/config-example.js

raw-image

※ 將程式碼修改成以下

 //調用函數並使用返回的結果:
const html = this.ecpayInstance.payment_client.aio_check_out_cvs
(cvsInfo, cvsPrams, inv_param, client_redirect_url)
return html;

程式碼解說:

  • const html = this.ecpayInstance.payment_client.aio_check_out_cvs(cvsInfo, cvsPrams, inv_param, client_redirect_url);
    • 這行程式碼使用 ECPay 實例的 payment_client 來呼叫 aio_check_out_cvs 方法,生成 CVS 支付的 HTML 表單,這個表單可以嵌入網頁,讓用戶進行支付。
    • 參數:
      • cvsInfo:包含 CVS 支付的相關資訊,例如訂單過期日期、描述等。cvsPrams:包含 CVS 支付的具體參數,例如商品資訊、金額等。inv_param:包含發票相關的參數(如果有)。client_redirect_url:支付完成後用戶將被重定向到的 URL。
  • return html
    • 將 html 作為函數的返回值,使得調用者可以接收並使用這個結果。

● 建立dispatcher資料夾管理多種支付:dispatcher → index.js

1.定義了一個異步函數 paymentDispatcher,用來處理支付相關的邏輯

import { PaymentProvider, PaymentWay } from "@/model/order"

//建立發派function
export const paymentDispatcher = async ({
paymentProvider,
paymentWay,
payload
}: {
paymentProvider: PaymentProvider;
paymentWay: PaymentWay;
payload: PaymentPayload
}) => {

}

程式碼解說:

1.函數定義

export const paymentDispatcher =
async ({ paymentProvider, paymentWay, payload }
: { paymentProvider: PaymentProvider; paymentWay: PaymentWay; payload: any })
=> { };
  • export:這個關鍵字使得 paymentDispatcher 函數可以在其他模組中被導入和使用。
  • const:使用 const 關鍵字宣告一個常數,表示 paymentDispatcher 的值不能被重新賦值。
  • async:這個關鍵字表明 paymentDispatcher 是一個非同步函數,該函數會返回一個 Promise,並且可以使用 await 關鍵字來處理非同步操作。

2.參數解構賦值

({ paymentProvider, paymentWay, payload })
  • 從傳入的參數對象中解構出 paymentProviderpaymentWaypayload 屬性。
  • paymentProvider:支付提供商的類型,可以是如 ECPay、PayPal 等不同的金流服務。
  • paymentWay:支付方式,例如信用卡、ATM、便利商店等。
  • payload:包含支付過程中所需的其他參數和資訊。

3.參數類型定義

: { paymentProvider: PaymentProvider; paymentWay: PaymentWay; payload: PaymentPayload }

為解構賦值參數提供類型定義,確保傳入的參數符合預期的結構和類型。

2.根據指定的支付提供商 (PaymentProvider) 和支付方式 (PaymentWay),生成一個 CVS 支付的 HTML 表單

const ecpay = new ECPayAdapter();

if (paymentProvider === PaymentProvider.ECPAY) {
if (paymentWay === PaymentWay.CVS) {
const html = ecpay.createCVS({
cvsPrams: {
MerchantTradeNo: payload.billId,
MerchantTradeDate: dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss"),
TotalAmount: String(payload.totalPrice),
TradeDesc: payload.desc,
ItemName: payload.details.map(content =>
`${content.name} x ${content.price}`).join("#"),
ReturnURL: payload.returnUrl,

},

});
return html;
} else throw new Error('No suitable payment way.');

程式碼解說:

1.建立 ECPayAdapter 實例

const ecpay = new ECPayAdapter();

這行程式碼創建了一個新的 ECPayAdapter 實例,名為 ecpay

2.判斷支付提供商

if (paymentProvider === PaymentProvider.ECPAY) {}

判斷 paymentProvider 是否為 PaymentProvider.ECPAY,即檢查支付提供商是否為 ECPay。

3.判斷支付方式

if (paymentWay === PaymentWay.CVS) {}

判斷 paymentWay 是否為 PaymentWay.CVS,即檢查支付方式是否為 CVS。

4.生成 CVS 支付表單

const html = ecpay.createCVS({
cvsPrams: {
MerchantTradeNo: payload.billId, // 商戶交易編號
MerchantTradeDate: dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss"), // 交易日期
TotalAmount: String(payload.totalPrice), // 總金額(這裡似乎應該是金額,而不是交易編號,可能需要確認)
TradeDesc: payload.desc, // 交易描述
ItemName: payload.details.map(content =>
`${content.name} x ${content.price}`).join("#"), // 商品名稱及價格
ReturnURL: payload.returnUrl // 返回 URL
}
});
return html;
  • MerchantTradeNo:從 payload 中獲取的商戶交易編號。
  • MerchantTradeDate:使用 dayjs 庫格式化當前日期和時間為 "YYYY-MM-DD HH:mm:ss" 格式。
  • 安裝 dayjs
npm install dayjs
  • 匯入 dayjs
import dayjs from 'dayjs';
  • TotalAmount:總金額,這裡使用 payload.totalPrice,檢查這是否為正確的金額值。
  • TradeDesc:交易描述,從 payload.desc 獲取。
  • ItemName:商品名稱及價格,使用 map 函數將 payload.details 中每個商品的名稱和價格組合成 name x price 的格式,並用 # 分隔。
  • ReturnURL:支付完成後返回的 URL,從 payload.returnUrl 獲取。
  • 當支付提供商為 ECPay 且支付方式為 CVS 時,使用 ECPay 的 createCVS 方法生成 CVS 支付的 HTML 表單。
  • 使用 payload 中的數據來填充 CVS 支付參數(cvsPrams)。

5.處理不合適的支付方式

} else throw new Error('No suitable payment way.');
  • 如果支付方式不合適,則拋出一個錯誤,提示 "No suitable payment way."。


3.定義PaymentPayload的接口用來定義支付資料的結構,確保物件的屬性和類型符合預期。

export interface PaymentPayload {
billId: string;
totalPrice: number;
desc: string;
details: OrderDetail[];
returnURL: string;
}

程式碼解說:

  • export interface PaymentPayload
    • export:這個關鍵字表示這個接口可以被其他模組導入和使用。
    • interface:定義了一個接口,名為 PaymentPayload。
  • billId: string
    • 定義了一個名為 billId 的屬性,類型是字串。
    • 描述:這個屬性通常用於表示交易或訂單的唯一標識符。
  • totalPrice: number
    • 定義了一個名為 totalPrice 的屬性,類型是數字。
    • 描述:這個屬性用來表示交易的總金額。
  • desc: string
    • 定義了一個名為 desc 的屬性,類型是字串。
    • 描述:這個屬性用來表示交易的描述或說明。
  • details: OrderDetail[]
    • 定義了一個名為 details 的屬性,類型是 OrderDetail 類型的陣列。
    • 描述:這個屬性用來表示交易中的每個商品的詳細信息。OrderDetail 是一個自定義的類型,用來描述每個商品的詳細內容。
  • returnUrl: string
    • 定義了一個名為 returnUrl 的屬性,類型是字串。
    • 描述:這個屬性用來表示支付完成後,用戶將被重定向到的 URL。

4.定義OrderDetail的接口接口用來描述單個商品的詳細信息,包括價格、名稱和數量。

interface OrderDetail {
price: string;
name: string;
amount: number;
}

程式碼解說:

  • interface OrderDetail
    • interface:定義了一個接口,名為 OrderDetail。
    • 接口是一種用來定義物件結構的方式,確保物件符合預期的屬性和類型。
  • price: string
    • 定義了一個名為 price 的屬性,類型是字串。
    • 描述:這個屬性用來表示商品的價格。將價格定義為字串類型允許它包含貨幣符號或小數點。
  • name: string
    • 定義了一個名為 name 的屬性,類型是字串。
    • 描述:這個屬性用來表示商品的名稱。
  • amount: number
    • 定義了一個名為 amount 的屬性,類型是數字。
    • 描述:這個屬性用來表示商品的數量。

※ 設計ECPAY control:

●controller →orderController.ts:(目的在創建訂單和支付請求)

const result = await paymentDispatcher({
paymentProvider,
paymentWay,
payload: {
billId: uid,
totalPrice,
desc: `create order bill from ${uid} with ${contents
.map((content) => content.productId)
.join(",")
}`,
returnURL: `${process.env.END_POINT}/orders/update`,
details: contentInfos || [],
}
})

程式碼解說:

1.等待非同步操作完成await 關鍵字用來等待 paymentDispatcher 函數返回一個 Promise,並在該 Promise 被解決(resolved)後返回結果。

2.處理非同步函數的返回值paymentDispatcher 函數完成並給出結果時,這個結果會被存到 result 變數。就可以在後續的程式碼中使用 result 了。

3.金流 API 的呼叫使用 paymentDispatcher 函數來處理金流 API 的串接。

4.paymentProviderpaymentWay這兩個變數指定了支付提供商(如 ECPAY)和支付方式(如 CVS)。

5.payloadpayload 是傳遞給 paymentDispatcher 函數的參數物件,包含了支付的相關資料。

6.billIduid 是一個唯一的識別碼,用於標識這筆支付交易。

7.totalPrice:表示支付的總金額。

8.desc:描述欄位裡面生成的字串說明了這筆訂單,包含了 uidcontents 中每個商品的 productId

9.returnURL:使用環境變數 process.env.END_POINT作為指定支付完成後返回的URL。

※ 環境變數.env

END_POINT='http://localhost:30000'
raw-image


10.details

  • 檢查 contentInfos 是否為 nullundefined:確保 details 屬性有一個預設值,避免因為 contentInfos 沒有值而引發錯誤。
  • 提供預設值:當 contentInfos 沒有值時,提供一個預設的空陣列 [],確保程式碼正常運行。

●controller →orderController.ts:(目的在保留與支付相關的商品名稱和價格)

 const contentInfos = contents.map(content => ({
name: ((products?.find(p => p.id === content.productId))?.name|| "",
price: content.price
}))

程式碼解說:

1.從 contents 陣列中提取訊息,並生成一個新的陣列 contentInfos。新的陣列包含每個商品的名稱和價格。

2.使用 map 函數迭代 contents 陣列

map 函數會為 contents 中的每個元素呼叫一次回調函數,並返回一個新的陣列。每個元素會被轉換為新陣列中的一個元素。

3.尋找商品名稱

  • 使用 find 函數在 products 陣列中查找與 content.productId 相匹配的商品。
  • 如果找到匹配的商品,find 函數會返回該商品物件;否則,返回 undefined

4.條件運算子 ?:

第一個 ?.:確保 products 為 null 或 undefined 時,不會引發錯誤。

第二個 ?.:確保 find 方法未找到匹配元素時,不會引發錯誤。

5.提供默認值

  • 當前面的鏈條返回 undefined 時,使用 || "" 提供一個默認值空字串。
  • 這樣可以確保 name 屬性永遠有一個值,即使查找失敗或 productsnullundefined值而導致的錯誤。

6.生成 contentInfos 陣列的元素

為新陣列 contentInfos 中的每個元素建立一個物件,包含商品名稱和價格。

●controller →orderController.ts:(目的在擷取產品 ID)

const products = await this.productModel.findByIds
(contents.map(product => product.productId))

程式碼解說:

1.擷取產品 ID

contents.map(product => product.productId)

使用 map 函數從 contents 陣列中提取每個商品的 productId,並生成一個新的包含所有 productId 的陣列。

2.查找產品資料

const products = await this.productModel.findByIds
(contents.map(product => product.productId));
  • 使用 findByIds 方法,將提取到的 productId 陣列作為參數傳入,從資料庫中查找所有與這些 productId 匹配的產品資料。
  • await 關鍵字表示這是一個非同步操作,會等待查詢操作完成後再繼續執行下一步。

●model →product.ts:(目的在從資料庫中查找與指定 ID 列表匹配的產品)

 findByIds(ids: string[], trx?: Knex.Transaction): Promise<Product[] | null>
raw-image


程式碼解說:

1.定義函數

findByIds(ids: number[], trx?: Knex.Transaction): Promise<Product[] | null>

這個函數定義接受一個包含多個產品 ID 的字串陣列(ids)和一個可選的資料庫交易(trx)。

並返回一個 Promise,該 Promise 解析為 Product 類型的陣列或 null

2.參數

  • ids: number[]:要查找的產品 ID 列表,這個陣列包含多個產品的 ID。
  • trx?: Knex.Transaction:可選的 Knex 交易物件,用於確保所有資料庫操作在一個交易中執行。

●model →product.ts:(目的在根據 ID 列表從資料庫中查找產品。)

public findByIds: IProductModel['findByIds'] = async (ids, trx) => {
let queryBuilder = this.knexSql(this.tableName).whereIn('id', ids)

if (trx) queryBuilder = queryBuilder.transacting(trx);

const result = await queryBuilder;

if (isEmpty(result)) return null

return result.map(this.DBData2DataObject) as Product[];
}

程式碼解說:

1.函數定義

public findByIds: IProductModel['findByIds'] = async (ids, trx) => {}
  • findByIdsIProductModel 介面中的方法。
  • 它是個非同步函數(async),接受兩個參數:ids 是一個字串陣列,包含需要查找的產品 ID;trx 是一個可選的 Knex 交易物件。

2.建立查詢構建器

let queryBuilder = this.knexSql(this.tableName).whereIn('id', ids);

使用 Knex 建立一個查詢構建器(queryBuilder),從資料表中查找 ID 在 ids 列表中的產品。

3.事務處理

if (trx) queryBuilder = queryBuilder.transacting(trx);

如果傳入了 trx 參數,將查詢構建器與該交易物件關聯,確保所有操作在同一個事務中執行。

4.執行查詢

const result = await queryBuilder;

使用 await 等待查詢執行並獲取結果。

5.檢查結果是否為空

if (isEmpty(result)) return null;

使用 lodash 的 isEmpty 函數檢查查詢結果是否為空。如果結果為空,返回 null。

6.轉換結果

return result.map(this.DBData2DataObject) as Product[];

如果結果不為空,使用 map 方法將每個資料庫返回的記錄轉換為 Product 物件,並返回轉換後的陣列。

●model →base.ts:

publicprotected 的變更:目的在限制DBData2DataObject存取範圍。

raw-image

●controller →orderController.ts:(目的在透過 Express 框架將 JSON 格式的回應傳送給客戶端)

 //金流API的串接
const result = await paymentDispatcher({
paymentProvider,
paymentWay,
payload: {
billId: uid,
totalPrice,
desc: `create order bill from ${uid} with ${contents
.map((content) => content.productId)
.join(",")
}`,
returnURL: `${process.env.END_POINT}/orders/update`,
details: contentInfos || [],
},
});
res.json({ status: 'success', data: result })
});
} catch (err) {
res.status(500).json({ errors: err })
throw err;
}

程式碼解說:

 res.json({ status: 'success', data: result })
  • res.json() 方法用來傳送 JSON 格式的資料作為回應。這是一種方便的方法來設定 Content-Typeapplication/json 並傳送 JSON 資料。
  • status: 顯示回應狀態。在這裡,它設為 'success',表示操作成功。
  • data: 包含操作結果。這裡,result 是之前由 paymentDispatcher 函數返回的結果。

※ 產生ECPAY 前端畫面:

●javascripts →index.js:(目的在將從 result 中獲取的 HTML 資料顯示在頁面上)

  const { data: html } = result;
this.ecpayHtml = html;

this.$nextTick(() => {
document.getElementById('_form_aiochk').submit();
})
console.log("--->", this.ecpayHtml);
},
},

程式碼解說:

1.解構賦值

const { data: html } = result; 

result 物件中的 data 屬性解構出來,並賦值給變數 html

2.設定成員變數

this.ecpayHtml = html;

html 資料賦值給 this.ecpayHtml,這樣可以在 Vue 中使用該資料。

3.使用 nextTick

this.$nextTick(() => {
document.getElementById('_form_aiochk').submit();
});
  • this.$nextTick 是 Vue 中的一個方法,確保 DOM 更新後自動提交表單。
  • 在回調函數中,通過 document.getElementById('_form_aiochk').submit(); 處理付款後自動提交具有 ID _form_aiochk 的表單。

4.除錯輸出

console.log("--->", this.ecpayHtml);

輸出 this.ecpayHtml 到瀏覽器控制台,以便檢查 html 資料的內容。

●javascripts →index.js:(目的在初始化組件的資料狀態)

return {
ecpayHtml: '',
};

程式碼解說:

  • 返回一個物件,其中包含 ecpayHtml 屬性,初始值設為空字串 ''
  • 在 Vue 組件中,這通常是在 data 方法中使用,用來初始化組件的資料屬性。
  • 初始化 ecpayHtml 為空字串是為了在組件創建時有個預設值,這樣後面可以正確地更新和使用這個屬性。

●views →index.ejs:(目的在插入動態的 HTML 內容)

<div id="ecpay" v-html="ecpayHtml"></div>

程式碼解說:

1.v-html 指令:

v-html 是 Vue.js 提供的一個指令,用來在元素內部動態插入 HTML 內容。

2.ecpayHtml 變數:

ecpayHtml 是一個包含 HTML 字串的變數。Vue.js 會自動更新 div 元素內部的內容,使其顯示最新的 HTML。

3.id="ecpay" 可以在 JavaScript 中使用 document.getElementById('ecpay') 精確定位這個 div 元素,便於進行後續操作。

●測試結果

1.按下按鈕

raw-image

2.畫面結果

raw-image

3.取得繳費代碼

raw-image

※ 確認支付流程:

●廠商後台管理

1.登入綠界科技的開發者測試後台

raw-image

2.確認MySQL的訂單

raw-image

3.進入一般訂單查詢全方位金流訂單,付款方式選擇超商代碼,然後按查詢

raw-image

4.出現訂單結果

raw-image
raw-image

●驗證支付流程是否正確:按下模擬付款

raw-image





全端網頁開發專業知識分享
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
※ 在前端畫面顯示ECPay: ● 優化前端:views → index.ejs <!-- 新增按鈕 --> <div> <button>綠界支付</button> </div> ● 在Vue 組件的資料定義中,新增buyItem來存儲購買的項目和數量:java
※ 綠界科技API平台: https://www.ecpay.com.tw/ ● 進到開發者中心: ● API串接規格文件: ● 使用SDK來串接Node.JS 的WEB SERVICE: ● 綠界 全方位(All In One)金流介接Node.js 第一版: 載點:htt
※ 利用transactionHandler將資料寫入database ● 在orderController.ts檔案中使用 try...catch 區塊:  try { } catch (err) { // 處理錯誤 res.status(500).json({ errors: e
※ 在orderController.ts檔案中定義createOrder:  public createOrder: IOrderController['createOrder'] = (req, res, _next) => { let { paymentProvider, paym
※ 訂單開立流程 → 完結的流程 前端資料驗證。 將商品的數量寫入(預扣)→ id。 利用ID去打第三方金流的API來產生第三方金流的訂單。 當使用者繳完錢之後,第三方金流他會打我們提供的update資訊的API。 ※ 從前端需要傳入的資料: 商品ID、商品數量、使用哪個payment
※ 路由設定: 在routes資料夾中,建立product.ts: ※ 設置產品相關的路由: 1.匯入模組: import express from 'express'; import { ControllerContext } from '@/manager/controllerM
※ 在前端畫面顯示ECPay: ● 優化前端:views → index.ejs <!-- 新增按鈕 --> <div> <button>綠界支付</button> </div> ● 在Vue 組件的資料定義中,新增buyItem來存儲購買的項目和數量:java
※ 綠界科技API平台: https://www.ecpay.com.tw/ ● 進到開發者中心: ● API串接規格文件: ● 使用SDK來串接Node.JS 的WEB SERVICE: ● 綠界 全方位(All In One)金流介接Node.js 第一版: 載點:htt
※ 利用transactionHandler將資料寫入database ● 在orderController.ts檔案中使用 try...catch 區塊:  try { } catch (err) { // 處理錯誤 res.status(500).json({ errors: e
※ 在orderController.ts檔案中定義createOrder:  public createOrder: IOrderController['createOrder'] = (req, res, _next) => { let { paymentProvider, paym
※ 訂單開立流程 → 完結的流程 前端資料驗證。 將商品的數量寫入(預扣)→ id。 利用ID去打第三方金流的API來產生第三方金流的訂單。 當使用者繳完錢之後,第三方金流他會打我們提供的update資訊的API。 ※ 從前端需要傳入的資料: 商品ID、商品數量、使用哪個payment
※ 路由設定: 在routes資料夾中,建立product.ts: ※ 設置產品相關的路由: 1.匯入模組: import express from 'express'; import { ControllerContext } from '@/manager/controllerM
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
※ 什麼是Web API API 就是後端開出來讓前端來用的介面,讓前端與後端可以溝通。 API流程: 終端使用者用任何一種裝置進入瀏覽器。 瀏覽器透過 API 向後端發出請求,請求查詢或修改資料。 後端透過 API 收到前端的請求後,取得資料並回應給前端。 前端渲染畫面,終端使用者
Thumbnail
你好,在下最近在學習開發web,學了html css js,也得出一些心得,由於網路上已有許多教學,所以我會著重在如何開發出to do List,以及解釋我寫的程式碼。相關的教學我會直接貼網址。如果我有什麼地方出錯,或者是可以寫得更好,歡迎在下方留言,討論。 首先先介紹我的開發環境: 我用了vs
※ 補充說明: ※ npm 常用指令: ◦ npm init–y:快速初始化一個新的 Node.js 並建立一個 package.json 文件的命令。 ◦ npm info 套件名稱 version:快速查詢指定 npm 套件的最新版本號。 ◦ npm install套件名稱:用來安裝
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
※ Express 專案步驟筆記清單 Node.js 環境建置核對 新增專案資料夾 設定 package.json npm init -y 設定程式入口為 app.js 安裝 Express:npm install express 設定主程式 app.js 建構應用程式伺服器 設定
Thumbnail
Accept:用戶端能夠接收的內容類型。 Accept: text/plain, text/html Accept-Charset:瀏覽器可以接受的字元編碼集。 Accept-Charset: utf8 Accept-Encoding:指定瀏覽器可以支援的web伺服器返回內容壓縮編碼
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
※ 什麼是Web API API 就是後端開出來讓前端來用的介面,讓前端與後端可以溝通。 API流程: 終端使用者用任何一種裝置進入瀏覽器。 瀏覽器透過 API 向後端發出請求,請求查詢或修改資料。 後端透過 API 收到前端的請求後,取得資料並回應給前端。 前端渲染畫面,終端使用者
Thumbnail
你好,在下最近在學習開發web,學了html css js,也得出一些心得,由於網路上已有許多教學,所以我會著重在如何開發出to do List,以及解釋我寫的程式碼。相關的教學我會直接貼網址。如果我有什麼地方出錯,或者是可以寫得更好,歡迎在下方留言,討論。 首先先介紹我的開發環境: 我用了vs
※ 補充說明: ※ npm 常用指令: ◦ npm init–y:快速初始化一個新的 Node.js 並建立一個 package.json 文件的命令。 ◦ npm info 套件名稱 version:快速查詢指定 npm 套件的最新版本號。 ◦ npm install套件名稱:用來安裝
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
※ Express 專案步驟筆記清單 Node.js 環境建置核對 新增專案資料夾 設定 package.json npm init -y 設定程式入口為 app.js 安裝 Express:npm install express 設定主程式 app.js 建構應用程式伺服器 設定
Thumbnail
Accept:用戶端能夠接收的內容類型。 Accept: text/plain, text/html Accept-Charset:瀏覽器可以接受的字元編碼集。 Accept-Charset: utf8 Accept-Encoding:指定瀏覽器可以支援的web伺服器返回內容壓縮編碼