網址:https://developer.paypal.com/studio/checkout/standard/getstarted
//在開發和測試階段模擬或測試 PayPal 結帳流程
npm install --save-dev @paypal/checkout-server-sdk
//導入 PaymentPayload:
import { PaymentPayload } from "@/dispatcher"
//引入 PayPal SDK:
const paypal = require("@paypal/paypal-server-sdk");
//定義 IPaypalAdapter 介面:
export interface IPaypalAdapter {
createOrder({
totalPrice,
details,
}: Omit<PaymentPayload, "desc" | "returnURL">): Promise<string>;
}
程式碼解說:
1.導入 PaymentPayload:從 "@/dispatcher" 模組中導入 PaymentPayload
介面,這個介面定義了支付資料的結構。
2.引入 PayPal SDK:引入了 @paypal/checkout-server-sdk
,用來與 PayPal 的 REST API 進行互動。
3.定義 IPaypalAdapter 介面:
createOrder
方法。desc
和 returnURL
屬性的 PaymentPayload
物件,並且返回一個 Promise<string>
Promise
使得 createOrder
方法可以處理非同步操作。export class PaypalAdapter implements IPaypalAdapter {
private paypalClient: any;
constructor() {
const Environment = process.env.NODE_ENV === "production"
? paypal.core.LiveEnvironment
: paypal.core.SandboxEnvironment;
}
}
程式碼解說:
1.宣告了一個名為 PaypalAdapter
的類別,實現了 IPaypalAdapter
介面,並使其可在其他模組中引用。
2.宣告了一個名為 paypalClient
的私有變數,使用 any
類型,並且只能在類別內部訪問。
3.構造函數初始化 PaypalAdapter
類別,並從環境變數中取得 NODE_ENV
的值以設置應用程式的運行環境。例如 "development"(開發環境)或 "production"(生產環境)。
NODE_ENV="development"//設為開發環境
4.選擇環境:
NODE_ENV
等於 "production"
,則選擇 paypal.core.LiveEnvironment
,這是 PayPal 的生產環境,用於處理真實交易。NODE_ENV
不等於 "production"
(例如,它可能是 "development"
或其他值),則選擇 paypal.core.SandboxEnvironment
,這是 PayPal 的沙盒環境,用於測試交易而不會影響真實資金。export class PaypalAdapter implements IPaypalAdapter {
private paypalClient: any;
constructor() {
...
//PayPalHttpClient 實例
this.paypalClient = new paypal.core.PayPalHttpClient(
new Environment(
process.env.PAYPAL_CLIENT_ID,
process.env.PAYPAL_CLIENT_SECRET));
}
}
程式碼解說:
1.paypal.core.PayPalHttpClient
:這是 PayPal SDK 中的一個類,用於處理與 PayPal 伺服器的 HTTP 請求和回應。
2.new Environment()
:這是一個函數調用,用來設置與 PayPal API 互動的環境。這個環境可以是沙盒環境(Sandbox)或是生產環境(Live)。沙盒環境用於測試支付流程,而生產環境則用於實際支付操作。
3.process.env.PAYPAL_CLIENT_ID
:這是從環境變數中讀取 PayPal 的客戶端 ID (PAYPAL_CLIENT_ID
)。這個 ID 是你的應用程式在 PayPal 上的唯一標識,用於認證請求。
4.process.env.PAYPAL_CLIENT_SECRET
:這是從環境變數中讀取 PayPal 的客戶端密鑰 (PAYPAL_CLIENT_SECRET
)。這個密鑰是用來保證 API 請求的安全性,只有你和 PayPal 知道。
export class PaypalAdapter implements IPaypalAdapter {
private paypalClient: any;
...
//實作createOrder
public createOrder: IPaypalAdapter["createOrder"] = async ({
billId,
totalPrice,
details,
}) => { };
}
程式碼解說:
1.public createOrder: IPaypalAdapter["createOrder"]
:
public
關鍵字表示這個方法是公開的,可以從類的外部調用。createOrder
是這個方法的名稱。IPaypalAdapter["createOrder"]
表示這個方法的類型。IPaypalAdapter
是一個接口,包含了 createOrder
方法的定義。2.
= async ({ billId, totalPrice, details }) => { };
:
= async
關鍵字表示這個方法是非同步的,意味著它會返回一個 Promise
,允許在函數內使用 await
關鍵字來處理非同步操作。
3.({ billId, totalPrice, details })
表示這個方法接受一個對象作為參數,這個對象包含了 billId
、totalPrice
和 details
這些屬性。
public createOrder: IPaypalAdapter["createOrder"] = async ({
...
}) => {
// call PayPal to set up a transaction
const request = new paypal.orders.OrdersCreateRequest();
};
}
程式碼解說:
建立了一個 OrdersCreateRequest
對象,該對象用於建立和發送訂單建立請求給PayPal。這是在整合PayPal的API時,處理支付訂單的一部分。
public createOrder: IPaypalAdapter["createOrder"] = async ({
...
}) => {
// call PayPal to set up a transaction
...
//訂單請求的首選回應格式
request.prefer("return=representation");
};
}
程式碼解說:
prefer
方法用於設置 HTTP 請求首選項標頭,當發送請求給 PayPal 時,它會告訴 PayPal 您希望以什麼樣的格式接收回應。"return=representation"
表示在成功建立訂單後,PayPal 返回訂單的完整表示(representation),而不是只返回一些簡要的訊息(例如訂單 ID)。request.requestBody({
intent: "CAPTURE",
purchase_units: [{
custom_id: billId,
amount: {
currency_code: 'USD',
value: totalPrice,
breakdown: {
item_total: {
currency_code: 'USD',
value: totalPrice,
},
},
},
items: details.map((item) => ({
name: item.name,
description: item.desc,
unit_amount: {
currency_code: 'USD',
value: item.price,
},
quantity: item.amount,
}))
},],
});
程式碼解說:
1.intent: "CAPTURE"
: 表示訂單的目標是進行資金擷取,即完成支付。
2.purchase_units
: 這是一個包含所有購買單位的陣列。
3.custom_id: billId
: custom_id
是您設置的自定義 ID,可能用於追踪或標識訂單。billId
是一個變數,應該在程式碼的其他地方進行定義。
4.amount
: 定義了整個購買單位的金額和貨幣類型。
5.breakdown
: 這提供了金額的詳細分解。
6.items
: 這是一個物品的列表,每個物品代表購物車中的一件商品。details.map(item => ( ... ))
這段程式碼將 details
陣列中的每個物品轉換為所需格式。
description: item.desc
: 取自 item
的 desc
屬性,商品說明。//定義OrderDetail
interface OrderDetail {
name: string;
price: number;
amount: number;//新增
desc: string;//新增
}
程式碼解說:
amount: number;
: 這個新屬性是一個數字,表示訂單的數量或數額。desc: string;
:這個新屬性是一個字串,表示訂單的描述。const contentInfos = contents.map((content) => {
const product = products?.find((p) => p.id === content.productId);
return {
name: product?.name || "",
price: content.price,
amount: content.amount,
desc: product?.description || "",
};
});
程式碼解說:
1.遍歷 contents 陣列:map
方法對 contents
陣列中的每個 content
元素執行函式,產生一個新的陣列 contentInfos
。
2.尋找對應的 product:在 products
陣列中尋找 id
與 content.productId
相符的 product
元素。如果 products
為 null
或 undefined
,則結果為 undefined
。
3.回傳一個新的物件:
name: product?.name || ""
: 如果找到 product
,則取 product.name
,否則為空字串。price: content.price
: 取自 content
的 price
屬性。amount: content.amount
: 取自 content
的 amount
屬性。desc: product?.description
: 如果找到 product
,則取 product.description
,否則為空字串。//實作createOrder
public createOrder: IPaypalAdapter["createOrder"] = async ({
billId,
totalPrice,
details,
}) => {
...
let order;
try {
order = await this.paypalClient.execute(request);
//Return a successful response to the client with the order ID
return order.result.id;
} catch (err: any) {
//Handle any errors from thr call
console.error(err);
throw new Error(err);
}
};
程式碼解說:
1.宣告 order 變數:宣告了一個 order
變數,但尚未賦值。
2.執行請求:
await
等待 PayPal 客戶端執行請求(request
)包括訂單 ID、付款細節等。這是個非同步操作,並將結果賦值給 order
變數。order.result.id
作為成功響應。3.捕捉和處理錯誤:
catch
區塊。//建立發派function
export const paymentDispatcher = async ({
paymentProvider,
paymentWay,
payload,
}: {
...
}) => {
const ecpay = new ECPayAdapter();
if (paymentProvider === PaymentProvider.ECPAY) {
...
} else if (paymentProvider === PaymentProvider.PAYPAL) {
//TODO
const paypal = new PaypalAdapter();
const id = await paypal.createOrder({
billId: payload.billId,
totalPrice: payload.totalPrice,
details: payload.details,
})
}
}
程式碼解說:
1.條件檢查:使用 else if
語句檢查 paymentProvider
是否為 PAYPAL
。
2.建立 PayPal 適配器:建立一個新的 PaypalAdapter
實例並將其存儲在 paypal
變量中。
3.建立訂單:調用 paypal.createOrder
方法,傳入包含 billId
、totalPrice
和 details
的 payload
物件。這個方法應該會與 PayPal 的 API 進行連線,以建立一個新訂單,並返回訂單的編號。
Business就是default App的Sandbox帳號,也就是開店用的虛擬帳號。
選擇View/Edit account
PayPal balance:顯示的金額就表示有多少錢