首頁 >web前端 >js教程 >深入解析:PayPal整合第二部分:PayPal REST API

深入解析:PayPal整合第二部分:PayPal REST API

WBOY
WBOY原創
2023-08-29 12:45:01874瀏覽

深入解析:PayPal整合第二部分:PayPal REST API

在本教學中,我將向您展示如何使用 PayPal REST API 和 C# 進行付款。他們針對 Ruby、Node.js、Python、PHP 等不同語言提供的所有函式庫都非常相似,因此這裡的所有概念都適用於所有函式庫。

項目設定

首先,我在 Visual Studio 2015 中建立了一個 MVC 專案:檔案 > 新建 > 專案,然後選擇 ASP.NET 應用程式

深入解析:PayPal整合第二部分:PayPal REST API

選擇 ASP.NET 5 Web 應用程式 模板,該模板使用新的 MVC 6。如果你熟悉的話,它與 MVC 5 類似。

深入解析:PayPal整合第二部分:PayPal REST API

正如您在下面的照片中看到的,我已向解決方案添加了一些文件和資料夾。主要需要注意的兩件事是:

  1. 參考中,我刪除了目標 DNX Core 5.0,它允許我們在 Mac OS X 或 Linux 中運行此項目,但我們需要的 PayPal 庫尚未更新然而。
  2. 我新增了「Services」資料夾,我將在其中封裝 PayPal 呼叫的邏輯,這樣我們就可以保持控制器簡潔明了。

深入解析:PayPal整合第二部分:PayPal REST API

使用 NuGet 安裝 PayPal SDK。 右鍵點擊解決方案名稱並選擇管理 NuGet 套件,然後搜尋「PayPal」並安裝它。

深入解析:PayPal整合第二部分:PayPal REST API

建立 PayPal 應用程式

#要將我們的應用程式與 PayPal 集成,我們需要導航到 PayPal Developers,然後在 REST API 應用程式下,點擊建立應用程式

深入解析:PayPal整合第二部分:PayPal REST API

為您的應用程式命名並選擇與該應用程式關聯的沙盒開發者帳戶。出於測試目的,我們可以導航到 http://sandbox.paypal.com 並使用沙箱登錄詳細資訊進行登錄,以查看測試 PayPal 帳戶和交易。

深入解析:PayPal整合第二部分:PayPal REST API

點擊建立應用程式後,我們會看到包含客戶端 ID 和秘密令牌的確認畫面.

深入解析:PayPal整合第二部分:PayPal REST API

將 clientId 和 clientSecret 令牌複製到 appsettings.json,如螢幕截圖所示如下:

深入解析:PayPal整合第二部分:PayPal REST API

測試付款

PayPal 提供了沙盒環境進行測試。您可以從那裡建立測試買家和賣家帳戶。註冊後,您將在沙盒中擁有一個與您的開發者帳戶綁定的企業帳戶。

要建立新的測試帳戶,請登入開發者網站,然後點擊控制面板標籤並導覽至沙盒 > 帳戶。 您可以在此處查看測試帳戶清單(如果有):

深入解析:PayPal整合第二部分:PayPal REST API

如果您尚未建立測試帳戶,請繼續並點擊建立帳戶在右上角,建立至少一個測試個人帳戶和一個測試企業帳戶。

深入解析:PayPal整合第二部分:PayPal REST API

建立測試帳戶後,您可以使用測試電子郵件地址和密碼透過 www.sandbox.paypal.com 登入您在上一個表格上指派給每個帳戶的資訊。這對於測試當您使用「個人測試帳戶」購買商品時資金是否會轉移到您的「測試企業帳戶」非常有用。現在您已準備好開始與 PayPal 整合並測試資金是否從一個帳戶轉移到另一個帳戶。

單一 PayPal 付款

PayPal 提供不同的付款方式。您可以使用直接信用卡付款,這表示您的客戶無法看到 PayPal 登入頁面或摘要 - 這一切都發生在您的網站上。為此,您需要符合 PCI 標準,我建議使用 Stripe,因為您只需要使用其 JavaScript 程式庫的 SSL。另一方面,要透過 PayPal 付款,需要三個步驟:

  1. 指定付款資訊以建立付款。
  2. 獲得付款批准,將您的客戶重新導向至 PayPal 以批准交易。
  3. 在 PayPal 將您的客戶重新導向回您的網站後,執行付款以獲取資金。

在我的 MVC 專案的 Services 資料夾中,我建立了 PayPalPaymentService 類,並在其中新增了以下方法:

#
public static Payment CreatePayment(string baseUrl, string intent)
{
    // ### Api Context
    // Pass in a `APIContext` object to authenticate 
    // the call and to send a unique request id 
    // (that ensures idempotency). The SDK generates
    // a request id if you do not pass one explicitly. 
    var apiContext = PayPalConfiguration.GetAPIContext();

    // Payment Resource
    var payment = new Payment()
    {
        intent = intent,    // `sale` or `authorize`
        payer = new Payer() { payment_method = "paypal" },
        transactions = GetTransactionsList(),
        redirect_urls = GetReturnUrls(baseUrl, intent)
    };

    // Create a payment using a valid APIContext
    var createdPayment = payment.Create(apiContext);

    return createdPayment;
}

private static List<Transaction> GetTransactionsList()
{
    // A transaction defines the contract of a payment
    // what is the payment for and who is fulfilling it. 
    var transactionList = new List<Transaction>();

    // The Payment creation API requires a list of Transaction; 
    // add the created Transaction to a List
    transactionList.Add(new Transaction()
    {
        description = "Transaction description.",
        invoice_number = GetRandomInvoiceNumber(),
        amount = new Amount()
        {
            currency = "USD",
            total = "100.00",       // Total must be equal to sum of shipping, tax and subtotal.
            details = new Details() // Details: Let's you specify details of a payment amount.
            {
                tax = "15",
                shipping = "10",
                subtotal = "75"
            }
        },
        item_list = new ItemList()
        {
            items = new List<Item>()
            {
                new Item()
                {
                    name = "Item Name",
                    currency = "USD",
                    price = "15",
                    quantity = "5",
                    sku = "sku"
                }
            }
        }
    });
    return transactionList;
}

private static RedirectUrls GetReturnUrls(string baseUrl, string intent)
{
    var returnUrl = intent == "sale" ? "/Home/PaymentSuccessful" : "/Home/AuthorizeSuccessful";

    // Redirect URLS
    // These URLs will determine how the user is redirected from PayPal 
    // once they have either approved or canceled the payment.
    return new RedirectUrls()
    {
        cancel_url = baseUrl + "/Home/PaymentCancelled",
        return_url = baseUrl + returnUrl
    };
}

public static Payment ExecutePayment(string paymentId, string payerId)
{
    // ### Api Context
    // Pass in a `APIContext` object to authenticate 
    // the call and to send a unique request id 
    // (that ensures idempotency). The SDK generates
    // a request id if you do not pass one explicitly. 
    var apiContext = PayPalConfiguration.GetAPIContext();
    
    var paymentExecution = new PaymentExecution() { payer_id = payerId };
    var payment = new Payment() { id = paymentId };

    // Execute the payment.
    var executedPayment = payment.Execute(apiContext, paymentExecution);

    return executedPayment;
}

此呼叫中傳遞了一些參數:

  • 意圖:三個可能的值:「銷售」用於立即付款,「授權」用於授權稍後捕獲付款,或「訂單」用於建立訂單。當您獲得授權稍後收取付款時,您有 3 天的保證期限,不過您最多可以在 29 天後嘗試收取付款。
  • 付款人:此付款的資金來源、所使用的付款方式 - PayPal 電子錢包付款、銀行直接簽帳卡或直接信用卡。
  • 交易:用於指定付款金額,並可選擇指定要支付的項目。如果需要,您也可以指定小計、運費和稅金。
  • 重定向網址:指定 PayPal 在交易後將您的客戶重新導向到的網址,以便您可以更新資料庫並顯示確認訊息。

可以從控制器中使用先前的功能,如下所示:

public IActionResult CreatePayment()
{
    var payment = PayPalPaymentService.CreatePayment(GetBaseUrl(), "sale");
    
    return Redirect(payment.GetApprovalUrl());
}

public IActionResult PaymentCancelled()
{
    // TODO: Handle cancelled payment
    return RedirectToAction("Error");
}

public IActionResult PaymentSuccessful(string paymentId, string token, string PayerID)
{
    // Execute Payment
    var payment = PayPalPaymentService.ExecutePayment(paymentId, PayerID);

    return View();
}

如您所見,我創建了三個操作:

  • CreatePayment:這是觸發付款的動作。它會呼叫 PayPal 以建立付款,然後將使用者重新導向到 PayPal 以批准交易。
  • 付款成功:這是 PayPal 在成功付款後將客戶重新導向回來的操作。此時,我們可以執行付款,將資金轉入我們的商家帳戶。
  • PaymentCancelled:如果使用者取消審核流程,則將使用者從 PayPal 重定向到此操作。此時,您可能希望讓客戶選擇重試或與您聯絡。

授權付款以便稍後取得

#此場景與前一個案例非常相似。如果您嘗試預訂尚未上市的產品,您可能需要使用此方法。獲得這筆款項的步驟是:

  1. 授權付款:此呼叫的「意圖」參數應為「授權」。
  2. 取得付款:請記住,授權最多可保證 3 天,但您可以嘗試取得最長 29 天的付款。

為了實現這種類型的付款,我只在 PayPalPaymentService 類別中新增了一個新方法來捕獲付款:

public static Capture CapturePayment(string paymentId)
{
    var apiContext = PayPalConfiguration.GetAPIContext();

    var payment = Payment.Get(apiContext, paymentId);
    var auth = payment.transactions[0].related_resources[0].authorization;

    // Specify an amount to capture.  By setting 'is_final_capture' to true, all remaining funds held by the authorization will be released from the funding instrument.
    var capture = new Capture()
    {
        amount = new Amount()
        {
            currency = "USD",
            total = "4.54"
        },
        is_final_capture = true
    };

    // Capture an authorized payment by POSTing to
    // URI v1/payments/authorization/{authorization_id}/capture
    var responseCapture = auth.Capture(apiContext, capture);

    return responseCapture;
}

然後,我在 HomeController 中新增了兩個新操作來顯示此類付款:

public IActionResult AuthorizePayment()
{
    var payment = PayPalPaymentService.CreatePayment(GetBaseUrl(), "authorize");
    
    return Redirect(payment.GetApprovalUrl());
}

public IActionResult AuthorizeSuccessful(string paymentId, string token, string PayerID)
{
    // Capture Payment
    var capture = PayPalPaymentService.CapturePayment(paymentId);

    return View();
}
  • AuthorizePayment 是觸發付款的動作。它與先前的“CreatePayment”函數非常相似,但在本例中我們將“authorize”作為意圖參數傳遞。
  • AuthorizeSuccessful 是在成功批准 PayPal 付款後將重新導向您的客戶的操作。此時,我正在捕獲付款,但您可以將 paymentId 保存在資料庫中,並在需要時捕獲付款。

在這些程式碼範例中,為了簡單起見,我對付款變數值進行了硬編碼。在您的實際應用程式中,您可能會將它們包裝在將所有這些值作為變數的方法中,以便您可以動態設定和重複使用所有內容。

訂閱

這在 PayPal 中稱為「結算計劃」 - 您可以建立定期付款計劃,並透過建立結算協議為您的客戶訂閱結算計劃。使用 PayPal REST API,您可以建立、更新或刪除結算計畫;如果您想建立一個管理面板來管理您的企業的這些事情,您可能會使用這個東西。

向客戶創建經常性費用的步驟如下:

  1. 建立結算計畫啟動它。建立結算計劃後,它會處於「已建立」狀態。它需要透過發出 PATCH 請求來啟動。
  2. 建立結算協定執行:對「建立結算協定」呼叫的回應包括指向approval_url和execute_url的連結。我們需要獲得計費協議的批准,然後執行計費協議。

計費計畫

建立結算計畫

建立定義計費週期的計費計畫。這是我們在建立計劃時需要傳遞的參數的摘要。

  • 名称:计费计划的名称。
  • 说明:计费计划的说明。
  • 类型:对于固定次数的定期付款,允许的值为“FIXED”;对于手动取消之前重复的计划,允许的值为“INFINITE”。
  • 商户首选项:这是一个指定首选项的对象,例如设置费用、付款的最大失败尝试次数、返回 URL、取消 URL、通知 URL,PayPal 在付款后将在其中重定向用户。李>
  • 付款定义:此计划的付款定义数组。通常,该数组将具有一两个付款定义。如果我们想提供免费试用或折扣价格试用,那么我们会设置两种付款定义。第一个定义是试用期,第二个定义是定期付款。付款定义的属性包括名称类型(试用或常规)、频率(日、周、月、年)、 >频率间隔(如果我们将频率设置为“周”并将频率间隔设置为“1”,则我们定义每周付款)、向客户收费的金额,以及cycles 是总付款次数。 收费模式用于指定计划金额之外的运费和税费。

这是一个代码片段,展示了如何创建计费计划:

// Define the plan and attach the payment definitions and merchant preferences.
// More Information: https://developer.paypal.com/webapps/developer/docs/api/#create-a-plan
var billingPlan = new Plan
{
    name = "Tuts+ Plus",
    description = "Monthly plan for courses.",
    type = "fixed",
    // Define the merchant preferences.
    // More Information: https://developer.paypal.com/webapps/developer/docs/api/#merchantpreferences-object
    merchant_preferences = new MerchantPreferences()
    {
        setup_fee = GetCurrency("0"), // $0
        return_url = "returnURL", // Retrieve from config
        cancel_url = "cancelURL", // Retrieve from config
        auto_bill_amount = "YES",
        initial_fail_amount_action = "CONTINUE",
        max_fail_attempts = "0"
    },
    payment_definitions = new List<PaymentDefinition>
    {
        // Define a trial plan that will only charge $9.99 for the first
        // month. After that, the standard plan will take over for the
        // remaining 11 months of the year.
        new PaymentDefinition()
        {
            name = "Trial Plan",
            type = "TRIAL",
            frequency = "MONTH",
            frequency_interval = "1",
            amount = GetCurrency("0"), // Free for the 1st month
            cycles = "1",
            charge_models = new List<ChargeModel>
            {
                new ChargeModel()
                {
                    type = "TAX",
                    amount = GetCurrency("1.65") // If we need to charge Tax
                },
                new ChargeModel()
                {
                    type = "SHIPPING",
                    amount = GetCurrency("9.99") // If we need to charge for Shipping
                }
            }
        },
        // Define the standard payment plan. It will represent a monthly
        // plan for $19.99 USD that charges once month for 11 months.
        new PaymentDefinition
        {
            name = "Standard Plan",
            type = "REGULAR",
            frequency = "MONTH",
            frequency_interval = "1",
            amount = GetCurrency("15.00"),
            // > NOTE: For `IFNINITE` type plans, `cycles` should be 0 for a `REGULAR` `PaymentDefinition` object.
            cycles = "11",
            charge_models = new List<ChargeModel>
            {
                new ChargeModel
                {
                    type = "TAX",
                    amount = GetCurrency("2.47")
                },
                new ChargeModel()
                {
                    type = "SHIPPING",
                    amount = GetCurrency("9.99")
                }
            }
        }
    }
};

// Get PayPal Config
var apiContext = PayPalConfiguration.GetAPIContext();

// Create Plan
plan.Create(apiContext);

新创建的结算计划处于 CREATED 状态。将其激活为“活动”状态,以便您的客户可以订阅该计划。要激活该计划,我们需要发出 PATCH 请求:

// Activate the plan
var patchRequest = new PatchRequest()
{
    new Patch()
    {
        op = "replace",
        path = "/",
        value = new Plan() { state = "ACTIVE" }
    }
};
plan.Update(apiContext, patchRequest);

如您所见,PayPal 库是其 REST API 的直接包装器,这很好,但与 Stripe 等其他 API 相比,该 API 也非常复杂。因此,将所有 PayPal 通信包装在对象中,为我们的应用程序提供更清晰、更简单的 API,这确实是一个不错的选择。在这里您可以看到封装在多个带有参数的函数中的代码的样子:

public static Plan CreatePlanObject(string planName, string planDescription, string returnUrl, string cancelUrl,
    string frequency, int frequencyInterval, decimal planPrice,
    decimal shippingAmount = 0, decimal taxPercentage = 0, bool trial = false, int trialLength = 0, decimal trialPrice = 0)
{
    // Define the plan and attach the payment definitions and merchant preferences.
    // More Information: https://developer.paypal.com/docs/rest/api/payments.billing-plans/
    return new Plan
    {
        name = planName,
        description = planDescription,
        type = PlanType.Fixed,

        // Define the merchant preferences.
        // More Information: https://developer.paypal.com/webapps/developer/docs/api/#merchantpreferences-object
        merchant_preferences = new MerchantPreferences()
        {
            setup_fee = GetCurrency("1"),
            return_url = returnUrl,
            cancel_url = cancelUrl,
            auto_bill_amount = "YES",
            initial_fail_amount_action = "CONTINUE",
            max_fail_attempts = "0"
        },
        payment_definitions = GetPaymentDefinitions(trial, trialLength, trialPrice, frequency, frequencyInterval, planPrice, shippingAmount, taxPercentage)
    };
}

private static List<PaymentDefinition> GetPaymentDefinitions(bool trial, int trialLength, decimal trialPrice,
    string frequency, int frequencyInterval, decimal planPrice, decimal shippingAmount, decimal taxPercentage)
{
    var paymentDefinitions = new List<PaymentDefinition>();

    if (trial)
    {
        // Define a trial plan that will charge 'trialPrice' for 'trialLength'
        // After that, the standard plan will take over.
        paymentDefinitions.Add(
            new PaymentDefinition()
            {
                name = "Trial",
                type = "TRIAL",
                frequency = frequency,
                frequency_interval = frequencyInterval.ToString(),
                amount = GetCurrency(trialPrice.ToString()),
                cycles = trialLength.ToString(),
                charge_models = GetChargeModels(trialPrice, shippingAmount, taxPercentage)
            });
    }

    // Define the standard payment plan. It will represent a 'frequency' (monthly, etc)
    // plan for 'planPrice' that charges 'planPrice' (once a month) for #cycles.
    var regularPayment = new PaymentDefinition
    {
        name = "Standard Plan",
        type = "REGULAR",
        frequency = frequency,
        frequency_interval = frequencyInterval.ToString(),
        amount = GetCurrency(planPrice.ToString()),
        // > NOTE: For `IFNINITE` type plans, `cycles` should be 0 for a `REGULAR` `PaymentDefinition` object.
        cycles = "11",
        charge_models = GetChargeModels(trialPrice, shippingAmount, taxPercentage)
    };
    paymentDefinitions.Add(regularPayment);

    return paymentDefinitions;
}

private static List<ChargeModel> GetChargeModels(decimal planPrice, decimal shippingAmount, decimal taxPercentage)
{
    // Create the Billing Plan
    var chargeModels = new List<ChargeModel>();
    if (shippingAmount > 0)
    {
        chargeModels.Add(new ChargeModel()
        {
            type = "SHIPPING",
            amount = GetCurrency(shippingAmount.ToString())
        });
    }
    if (taxPercentage > 0)
    {
        chargeModels.Add(new ChargeModel()
        {
            type = "TAX",
            amount = GetCurrency(String.Format("{0:f2}", planPrice * taxPercentage / 100))
        });
    }

    return chargeModels;
}

更新结算计划

您可以通过提出“PATCH”请求来更新现有结算方案的信息。这是一个包装该调用的函数:

public static void UpdateBillingPlan(string planId, string path, object value)
{
    // PayPal Authentication tokens
    var apiContext = PayPalConfiguration.GetAPIContext();

    // Retrieve Plan
    var plan = Plan.Get(apiContext, planId);

    // Activate the plan
    var patchRequest = new PatchRequest()
    {
        new Patch()
        {
            op = "replace",
            path = path,
            value = value
        }
    };
    plan.Update(apiContext, patchRequest);
}

要更新计费计划描述,我们可以调用此函数并传递正确的参数:

UpdateBillingPlan(
    planId: "P-5FY40070P6526045UHFWUVEI", 
    path: "/", 
    value: new Plan { description = "new description" });

删除结算计划

理想情况下,当您不想接受新客户加入结算计划时,您需要将其更新为“非活动”状态。这不会影响该计划的现有计费协议。只需调用 UpdateBillingPlan 函数即可完成此操作:

UpdateBillingPlan(
    planId: "P-5FY40070P6526045UHFWUVEI",
    path: "/",
    value: new Plan { state = "INACTIVE" });

计费协议

创建计费协议

创建一个或多个结算计划后,您希望开始让客户注册您的订阅计划。为此,您需要收集客户详细信息并向 PayPal 提出请求。为了能够测试此功能,我向 HomeController 添加了几个操作:

public IActionResult Subscribe()
{
    var plan = PayPalSubscriptionsService.CreateBillingPlan("Tuts+ Plan", "Test plan for this article", GetBaseUrl());

    var subscription = PayPalSubscriptionsService.CreateBillingAgreement(plan.id, 
        new PayPal.Api.ShippingAddress
        {
            city = "London", 
            line1 = "line 1",
            postal_code = "SW1A 1AA",
            country_code = "GB"
        }, "Pedro Alonso", "Tuts+", DateTime.Now);
    
    return Redirect(subscription.GetApprovalUrl());
}

public IActionResult SubscribeSuccess(string token)
{
    // Execute approved agreement
    PayPalSubscriptionsService.ExecuteBillingAgreement(token);

    return View();
}

public IActionResult SubscribeCancel(string token)
{
    // TODO: Handle cancelled payment
    return RedirectToAction("Error");
}
  • 订阅:这是调用的第一个操作。它正在创建一个测试结算计划,然后创建该计划的结算协议(订阅),并将用户重定向到 PayPal 以确认付款。
  • 订阅成功:此操作是成功订阅后用作“返回 URL”的操作。协议令牌标识符在查询字符串中传递,我们使用此令牌来执行计费协议并使其处于活动状态。
  • SubscribeCancel: 此操作用作“取消 URL”。如果由于某种原因付款失败,或者您的客户取消了 PayPal 付款,用户将采取此操作,您需要处理此问题。也许可以提供重试的选项。

正如您在前面的代码片段中看到的,我已将大部分功能包装在几个方法中。第一个是上一节中解释的“CreateBillingPlan”。第二个是“CreateBillingAgreement”,用于为用户订阅计划:

public static Agreement CreateBillingAgreement(string planId, ShippingAddress shippingAddress, 
    string name, string description, DateTime startDate)
{
    // PayPal Authentication tokens
    var apiContext = PayPalConfiguration.GetAPIContext();

    var agreement = new Agreement()
    {
        name = name,
        description = description,
        start_date = startDate.ToString("yyyy-MM-ddTHH:mm:ss") + "Z",
        payer = new Payer() { payment_method = "paypal" },
        plan = new Plan() { id = planId },
        shipping_address = shippingAddress
    };
    
    var createdAgreement = agreement.Create(apiContext);
    return createdAgreement;
}

第三种方法是“ExecuteBillingAgreement”。成功订阅批准后,我们​​使用返回的令牌来激活订阅:

public static void ExecuteBillingAgreement(string token)
{
    // PayPal Authentication tokens
    var apiContext = PayPalConfiguration.GetAPIContext();

    var agreement = new Agreement() { token = token };
    var executedAgreement = agreement.Execute(apiContext);
}

暂停计费协议

使用此方法暂停协议:

public static void SuspendBillingAgreement(string agreementId)
{
    var apiContext = PayPalConfiguration.GetAPIContext();

    var agreement = new Agreement() { id = agreementId };
    agreement.Suspend(apiContext, new AgreementStateDescriptor()
        { note = "Suspending the agreement" });
}

重新激活计费协议

这个与上一个非常相似:

public static void ReactivateBillingAgreement(string agreementId)
{
    var apiContext = PayPalConfiguration.GetAPIContext();

    var agreement = new Agreement() { id = agreementId };
    agreement.ReActivate(apiContext, new AgreementStateDescriptor()
        { note = "Reactivating the agreement" });
}

取消计费协议

使用此功能取消计划:

public static void CancelBillingAgreement(string agreementId)
{
    var apiContext = PayPalConfiguration.GetAPIContext();

    var agreement = new Agreement() { id = agreementId };
    agreement.Cancel(apiContext, new AgreementStateDescriptor()
        { note = "Cancelling the agreement" });
}

更新计费协议

这个选项非常有限,我希望从这次通话中可以更改订阅计划,以升级或降级客户。与 Stripe 不同,单次调用不支持此功能。您需要通过取消当前协议并创建新的升级或降级协议来处理这种情况。这并不理想,但将来可能会改变。

结论

这是人们用来与 PayPal 集成的最常用功能的概述。他们的 API 比本文中解释的集成方法要大得多 - 您还可以发放退款和部分退款,并且他们针对本文涵盖的示例中的边缘情况提供了许多不同的选项。如果您有兴趣获得有关任何特定集成的更多详细信息,请在评论中留下建议。

以上是深入解析:PayPal整合第二部分:PayPal REST API的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn