hakito/cakephp-paypal-checkout 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

hakito/cakephp-paypal-checkout

最新稳定版本:v1.3

Composer 安装命令:

composer require hakito/cakephp-paypal-checkout

包简介

PayPalCheckout plugin for CakePHP

README 文档

README

This plugin can be used for the backend part of the PayPal Checkout API.

Installation

You can install this plugin into your CakePHP application using composer.

The recommended way to install composer packages is:

composer require hakito/paypal-checkout

Load the plugin

In your plugins.php add

'PayPalCheckout' => [
    'routes' => true
],

Configuration

In your app.php you have to setup the api credentials:

'PayPalCheckout' => [
    //'Mode' => 'sandbox', // optional set the current mode
    'ClientId' => 'CLIENT ID',
    'ClientSecret' => 'CLIENT SECRET'
],

For some basic logging you can add this to the Log section:

'PayPalCheckout' => [
    'className' => FileLog::class,
    'path' => LOGS,
    'file' => 'PayPalCheckout',
    'scopes' => ['PayPalCheckout'],
    'levels' => ['warning', 'error', 'critical', 'alert', 'emergency', 'info'],
]

Usage

Implement the client side according to the api docs. Example

<?php

use Cake\Core\Configure;
?>
<script src="https://www.paypal.com/sdk/js?client-id=<?= Configure::read('PayPalCheckout.ClientId') ?>&currency=EUR&locale=de_AT"></script>
<script lang="javascript" type="text/javascript">
window.paypal
  .Buttons({
    style: {
        layout: 'horizontal',
        label: 'buynow'
    },
    async createOrder() {
      try {
        const response = await fetch("/paypal-checkout/Orders/create", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
        });

        const orderData = await response.json();

        if (orderData.abort) // example for handling your own data from the controller
        {
          if (orderData.abort == 'already paid')
          {
            window.location.reload();
          }
          else
            throw new Error(orderData.abort);
        }

        if (orderData.id) {
          return orderData.id;
        } else {
          const errorDetail = orderData?.details?.[0];
          const errorMessage = errorDetail
            ? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
            : JSON.stringify(orderData);

          throw new Error(errorMessage);
        }
      } catch (error) {
        console.error(error);
        resultMessage(`Could not initiate PayPal Checkout...<br><br>${error}`);
      }
    },
    async onApprove(data, actions) {
      try {
        const response = await fetch(`/paypal-checkout/Orders/capture/${data.orderID}`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
        });

        const orderData = await response.json();

        // Three cases to handle:
        //   (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
        //   (2) Other non-recoverable errors -> Show a failure message
        //   (3) Successful transaction -> Show confirmation or thank you message

        const errorDetail = orderData?.details?.[0];

        if (errorDetail?.issue === "INSTRUMENT_DECLINED") {
          // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
          // recoverable state, per https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/
          return actions.restart();
        } else if (errorDetail) {
          // (2) Other non-recoverable errors -> Show a failure message
          throw new Error(`${errorDetail.description} (${orderData.debug_id})`);
        } else if (!orderData.purchase_units) {
          throw new Error(JSON.stringify(orderData));
        } else {
          // (3) Successful transaction -> Show confirmation or thank you message
          // Or go to another URL:  actions.redirect('thank_you.html');
          console.log('redirecting');
          actions.redirect(orderData.redirectSuccessUrl);
        }
      } catch (error) {
        console.error(error);
        resultMessage(
          `Sorry, your transaction could not be processed...<br><br>${error}`,
        );
      }
    },
  })
  .render("#paypal-button-container");

// Example function to show a result to the user. Your site's UI library can be used instead.
function resultMessage(message) {
  const container = document.querySelector("#result-message");
  container.innerHTML = message;
}

</script>

Event handling

You have to handle two events in your application:

EventManager::instance()
    ->on('PayPalCheckout.CreateOrder', OrdersController::createPayPalOrder(...))
    ->on('PayPalCheckout.CaptureOrder', PaymentCallbacks::capturePayPalOrder(...));

CreateOrder events

Fired when a request for payment is built. You can setup the ordered items and amounts here.

The function has to return an array with the order data. Either build the array on your own or use the OrderBuilder.

public static function createPayPalOrder(Event $event)
{
    // Optional you can also send back custom data and handle it on the client side
    if (ORDER_ALREADY_AID)
        return ['abort' => ['already paid']];

    $purchaseUnitBuilder = new PurchaseUnitBuilder(new AmountBuilder('USD', '123.56'));
    $purchaseUnitBuilder->ReferenceId('YOUR ORDER'); // optionally set your order id here

    return (new OrderBuilder())
        ->add($purchaseUnitBuilder)
        ->Build();
}

CaptureOrder event

Fired when the payment has been completed.

public static function capturePayPalOrder(Event $event, $args)
{
    $data = $event->getData('body');
    if ($data->status != 'COMPLETED')
    {
        Log::error("Captured PayPal checkout payment is not COMPLETED but $data->status");
        return;
    }
    $purchaseUnit = $data->purchase_units[0];
    $orderId = $purchaseUnit->reference_id;
    // Capture ID for issuing refunds
    $captureId = $purchaseUnit->payments->captures[0]->id;
    // DO YOUR PAYMENT HANDLING HERE ($orderId, $captureId);
    return ['redirectSuccessUrl' => 'http://payment-success.example.com'];
}

Refund a payment

You can do a full refund of the payment in a controller.

$this->loadComponent('PayPalCheckout.Checkout', Configure::read('PayPalCheckout'));
$this->Checkout->refundPayment('CAPTURE_ID_OF_YOUR_ORDER');

统计信息

  • 总下载量: 96
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2024-01-07