PHP Paypal支付+退款全流程

1、安装依赖包

composer require paypal/rest-api-sdk-php

2、Paypal支付

<?php
namespace app\index\controller;
use think\Controller;

use PayPal\Api\Payer;
use PayPal\Api\Item;
use PayPal\Api\ItemList;
use PayPal\Api\Details;
use PayPal\Api\Amount;
use PayPal\Api\Transaction;
use PayPal\Api\RedirectUrls;
use PayPal\Api\Payment;
use PayPal\Api\Refund;
use PayPal\Api\Sale;
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Exception\PayPalConnectionException;
use PayPal\Rest\ApiContext;
use PayPal\Api\PaymentExecution;

//沙盒登录 https://www.sandbox.paypal.com/c2/signin
//参考文档 https://learnku.com/articles/26282
//paypal依赖包  composer require paypal/rest-api-sdk-php

class Paypal extends Controller
{
    const clientId = 'xxxxxxxxxxxxxxxxxx';//应用ID
    const clientSecret = 'xxxxxxxxxxxxxx';//应用秘钥
    const accept_url = 'Paypal/Callback'; //同步回调地址
    const error_log = './paypal_errors.log'; //错误日志
    const Currency = 'USD';//币种
    protected $PayPal;


    public function __construct()
    {
        $this->PayPal = new ApiContext(
            new OAuthTokenCredential(
                self::clientId,
                self::clientSecret
            )
        );
        //如果是沙盒测试环境不设置,请注释掉
        $this->PayPal->setConfig(
            array(
                'mode' => 'live',
            )
        );
    }

    /**
     * paypal支付
     * @param $price float 金额
     * @param $order_sn string 订单号
     * @param $product string 商品名
     * @param $description string 商品描述
     * @param $shipping float 运费
     *
     */
    public function pay($price=10, $order_sn=123456, $product='商品', $description='描述内容', $shipping = 0)
    {
        $paypal = $this->PayPal;
        $total = $price + $shipping;//总价
        $payer = new Payer();
        $payer->setPaymentMethod('paypal');
        $item = new Item();
        $item->setName($product)->setCurrency(self::Currency)->setQuantity(1)->setPrice($price);
        $itemList = new ItemList();
        $itemList->setItems([$item]);
        $details = new Details();
        $details->setShipping($shipping)->setSubtotal($price);
        $amount = new Amount();
        $amount->setCurrency(self::Currency)->setTotal($total)->setDetails($details);
        $transaction = new Transaction();
        $transaction->setAmount($amount)->setItemList($itemList)->setDescription($description)->setInvoiceNumber(uniqid());
        $redirectUrls = new RedirectUrls();
        $redirectUrls->setReturnUrl(self::accept_url . '?success=true&order_sn='.$order_sn)->setCancelUrl(self::accept_url . '/?success=false');
        $payment = new Payment();
        $payment->setIntent('sale')->setPayer($payer)->setRedirectUrls($redirectUrls)->setTransactions([$transaction]);
        try {
            $payment->create($paypal);
            $approvalUrl = $payment->getApprovalLink();
            echo json_encode(['code'=>200, 'msg'=>'成功!','url'=>$approvalUrl]); 
            //header("Location: {$approvalUrl}"); //自动跳转页面
            exit();
        } catch (PayPalConnectionException $e) {
            error_log(date('Y-m-d H:i:s')." paypal pay fail: ".$e->getData()."\n", 3, self::error_log);
            echo json_encode(['code'=>202, 'msg'=>$e->getData()]);
            exit();
        }
    }
}

3、同步回调

/**
     * 同步回调方法
     */
    public function Callback()
    {
        $success = trim($_GET['success']);
        if ($success == 'false' && !isset($_GET['paymentId']) && !isset($_GET['PayerID'])) {
            error_log(date('Y-m-d H:i:s')." paypal Callback fail: 取消付款"."\n", 3, self::error_log);
            echo json_encode(['code'=>202, 'msg'=>'取消付款']);
            exit();
        }
        $paymentId = trim($_GET['paymentId']);
        $PayerID = trim($_GET['PayerID']);
        $order_sn = trim($_GET['order_sn']); //订单号
        if (!isset($success, $paymentId, $PayerID)) {
            error_log(date('Y-m-d H:i:s')." paypal Callback fail: 支付失败"."\n", 3, self::error_log);
            echo json_encode(['code'=>202, 'msg'=>'支付失败']);
            exit();
        }
        if ((bool)$_GET['success'] === 'false') {
            error_log(date('Y-m-d H:i:s')." paypal Callback fail:".'支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】'."\n", 3, self::error_log);
            echo json_encode(['code'=>202,'msg'=>'支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】']);
            exit();
        }
        $payment = Payment::get($paymentId, $this->PayPal);
        $execute = new PaymentExecution();
        $execute->setPayerId($PayerID);

        try {
            $payment->execute($execute, $this->PayPal);
//            error_log(date('Y-m-d H:i:s')."同步回调:".json_encode($_GET)."\n", 3, self::error_log);

            /*
             * 将$paymentId保存到数据表
             * */

            echo json_encode(['code'=>200,'msg'=>'支付成功,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】,订单号【'.$order_sn.'】']);
            exit();
        } catch (Exception $e) {
            error_log(date('Y-m-d H:i:s')." paypal Callback fail:".$e->getMessage(). ",支付失败,支付ID【" . $paymentId . "】,支付人ID【" . $PayerID . "】"."\n", 3, self::error_log);
            echo json_encode(['code'=>202,'msg'=>$e . ',支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】']);
            exit();
        }
    }

4、异步回调

    public function notify(){ //异步回调(地址在paypal控制台设置)
        //通知结果文档 https://developer.paypal.com/docs/api-basics/notifications/webhooks/notification-messages/
        $json_data = file_get_contents('php://input');
//        error_log(date('Y-m-d H:i:s')."异步回调:".$json_data."\n", 3, self::error_log);
        $json_data = str_replace("'", '', $json_data);
        $json_data = json_decode($json_data,true);
        if(empty($json_data)){
            error_log(date('Y-m-d H:i:s')."paypal notify fail: 参加为空!"."\n", 3, self::error_log);
            exit();
        }
        try {
            //自己打印$json_data的值看有那些是你业务上用到的
            $id = $json_data['resource']['id'];  //付款id(退款用),存表
            $paymentId = $json_data['resource']['parent_payment']; //用此字段查询出订单表
            if($json_data['resource_type']=='sale'){ //支付完成

                /*
                * 处理相关业务
                * */

            }elseif ($json_data['resource_type']=='refund'){ //退款成功
                $total=$json_data['resource']['amount']['total']; //退款金额

                /*
                * 处理相关业务
                * */

            }
        } catch (\Exception $e) {
            //记录错误日志
            error_log(date('Y-m-d H:i:s')."paypal notify fail:".$e->getMessage()."\n", 3, self::error_log);
            exit();
        }
        return "success";
    }

5、退款

	public function returnMoney($txn_id = "3LV45487XU194815F",$price=1){ //异步回调sale通知中拿到的id
        try {
            $amt = new Amount();
            $amt->setCurrency('USD')
                ->setTotal($price);  // 退款的费用
            $refund = new Refund();
            $refund->setAmount($amt);
            $sale = new Sale();
            $sale->setId($txn_id);
            $refundedSale = $sale->refund($refund, $this->PayPal);
        } catch (\Exception $e) {
            // PayPal无效退款
//            return json_decode(json_encode(['message' => $e->getMessage(), 'code' => $e->getCode(), 'state' => $e->getMessage()]));  // to object
            echo json_encode(['code' => 202,'msg'=> $e->getMessage()]);
            exit();
        }
        $refundedSale = $refundedSale->toArray();
        // 退款完成
        if($refundedSale['state']=='completed'){
            echo json_encode(['code' => 200,'msg' =>'退款成功!']);
            exit();
        }else{
            echo json_encode(['code' => 202,'msg' =>'退款失败!']);
            exit();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值