PaypalPayment.php 6.73 KB
<?php
/**
 * Created by PhpStorm.
 * User: lishuai
 * Date: 2022/2/15
 * Time: 4:23 PM
 */

namespace App\Payment;

use App\Models\MembershipGood;
use App\Models\Order;
use App\Models\User;
use App\Models\UserProfile;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class PaypalPayment implements PaymentInterface
{

    const IS_SANDBOX = true;

    public $baseUrl = 'https://api-m.paypal.com';

    public $baseUrlSandbox = 'https://api-m.sandbox.paypal.com';


    public $clientId = 'AdDRE91WSp5q1fYLODpJduc2mRjA_v6E205SvkfVSOgvr98xLeyDCHY4OPAaSFMK1SHYOfJ4TksHSX1-';
    public $secret = 'EDoy_PVrFyobWt9DzAMRMikJwWCXenkWSx9CGFz0MxHt3a8fs6v-LnORMilIbftb2GwBKxOoTVZNBHNR';

    /** 访问令牌*/
    public $accessToken;

    public function __construct()
    {
        // 初始化时做一些准备工作
        $redis = Redis::connection();
        $access_token = $redis->get('paypal:access_token');
        if ($access_token){
            $this->accessToken = $access_token;
        }else{
            $client = new Client([
                'base_uri' => $this->baseUrlSandbox,
                'headers'=>[
                    'Content-Type' => 'application/x-www-form-urlencoded',
                    'Accept'=>'application/json',
                ]
            ]);
            $response = $client->post('/v1/oauth2/token',[
                'form_params'=>['grant_type' => 'client_credentials'],
                'auth' => [$this->clientId, $this->secret],
            ]);
            $body = $response->getBody();
            $content = json_decode($body->getContents(),true);
            $this->accessToken = $content['access_token'];
            $redis->setex('paypal:access_token',$content['expires_in'],$content['access_token']);
        }
    }

    /**
     * Setting up the JSON request body for creating the Order. The Intent in the
     * request body should be set as "CAPTURE" for capture intent flow.
     * @param  Order $order
     * @return array
     */
    private static function buildCreateOrderRequestBody(Order $order)
    {
        return array(
            'intent' => 'CAPTURE',    //CAPTURE 商家打算在客户付款后立即扣款。 AUTHORIZE 商家打算在客户付款后授权付款并暂停资金
            'application_context' =>
                array(
                    'return_url' => 'https://example.com/return',    // todo
                    'cancel_url' => 'https://example.com/cancel',    // todo 这里要询问reason app是否有schema,
                    'brand_name' => 'Parlando',    //覆盖 PayPal 网站上 PayPal 帐户中公司名称的标签。
                    'locale' => 'en-US',    //zh-CN
                    'landing_page' => 'NO_PREFERENCE',    //LOGIN / BILLING / NO_PREFERENCE
                    'shipping_preference' => 'NO_SHIPPING',    //运输偏好
                    'user_action' => 'PAY_NOW',
                ),
            'purchase_units' =>
                array(
                    0 =>
                        array(
                            'description' => $order->description,   // 购买说明
                            'custom_id' => $order->order_sn,    //API 调用者提供的外部 ID  可以理解为order_id  osnxxxxxx
                            'soft_descriptor' => 'PayPal Parlando Vip',    // 出现在付款人卡对帐单上的对帐单描述符的动态文本 最大长度:22.
                            'amount' =>
                                array(
                                    'currency_code' => 'USD',   // CNY
                                    'value' => $order->pay_amount,    //
                                ),
                        ),
                ),
        );
    }

    public function prepare(Order $order)
    {
        // 在PayPal上创建一个订单,它会返回一个订单对象,它有一个订单id
        $client = new Client([
            'base_uri' => $this->baseUrlSandbox,
            'headers'=>[
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $this->accessToken,
                'Prefer' => 'return=representation',
            ]
        ]);

        $response = $client->post('/v2/checkout/orders',[
            'json' => self::buildCreateOrderRequestBody($order),
        ]);
        $body = $response->getBody();
        $content = json_decode($body->getContents(),true);

        /** 更新订单交易号 */
        $order->pay_number = $content['id'];
        $order->pay_type = 'paypal';
        $order->save();

        return $content;
    }

    public function capture($orderId)
    {
        $client = new Client([
            'base_uri' => $this->baseUrlSandbox,
            'headers'=>[
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $this->accessToken,
                'Prefer' => 'return=representation',
            ]
        ]);

        $response = $client->post('/v2/checkout/orders/' . $orderId . '/capture');
        $body = $response->getBody();
        $content = json_decode($body->getContents(),true);


        // todo 应该使用队列,异步执行回调程序
        if (!$this->notify($orderId)) return false;

        return $content;
    }

    public function notify($orderId)
    {
        $order = Order::query()->where('pay_number',$orderId)->first();
        if (!$order) return false;

        /** 修改订单状态*/
        $order->pay_time = Carbon::now();
        $order->status = Order::PAID;
        $order->save();

        /** 给用户加会员*/
        $goods = MembershipGood::query()->find($order->order_goods->goods_id);
        if ($goods->limit_unit == '月') {
            $days = intval($goods->limit_days) * 30;// 计算天数
        } elseif ($goods->limit_unit == '年') {
            $days = intval($goods->limit_days) * 365;// 计算天数
        } else {
            $days = intval($goods->limit_days) * 1;// 计算天数
        }
        $user = UserProfile::query()->find($order->user_id);
        if ($user->is_vip == 0){
            $user->is_vip = 1;
            $user->create_vip_time = Carbon::now();
            $user->expire_vip_time = Carbon::now()->addDays($days);
        }else{
            if (Carbon::now()->gte($user->expire_vip_time)){    // 已经过期了
                $user->expire_vip_time = Carbon::now()->addDays($days);
            }else{
                $user->expire_vip_time = Carbon::parse($user->expire_vip_time)->addDays($days);
            }
        }

        $user->buy_number += 1;
        $user->buy_amount += $order->pay_amount;
        $user->last_buy_time = Carbon::now();
        $user->save();

        /** 修改订单状态*/
        $order->status = Order::DONE;
        $order->save();

        return true;
    }
}