创建产品和订阅计划

产品 和计划是可以共用的,不需要每个用户单独创建

后台添加产品和计划

正式:https://www.paypal.com/billing/plans

沙盒:https://www.sandbox.paypal.com/billing/plans
在这里插入图片描述

在这里插入图片描述
使用下面的计划ID去创建订阅
在这里插入图片描述

Api接口添加产品和计划
 /**
     * 创建订阅产品
     * @param $name
     * @return false|mixed
     */
    static function createProduct($name = '')
    {
        if (!$name) {
            return false;
        }
        $data = [
            "name" => $name,  // 产品名称
            "description" => "Monthly Premium Subscription",  // 产品说明
            "type" => "SERVICE", // PHYSICAL-实物商品 DIGITAL-数码商品 SERVICE-代表服务的产品
            "category" => "SOFTWARE", // 产品类别 SOFTWARE  | Entertainment and media
//            "image_url" => "https://example.com/streaming.jpg",  // 产品logo
//            "home_url" => "https://example.com/home" // 产品页面
        ];
        $accessToken = self::getToken();
        list($clientId, $clientSecret, $mode, $baseUrl) = self::getConfig();
        $ch = curl_init($baseUrl . "/v1/catalogs/products");
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                "Authorization: Bearer $accessToken",
                "Content-Type: application/json"
            ],
            CURLOPT_POSTFIELDS => json_encode($data)
        ]);
        $result = curl_exec($ch);
        curl_close($ch);
        return json_decode($result, true);
    }

    static function getProducts()
    {
        $accessToken = self::getToken();
        list($clientId, $clientSecret, $mode, $baseUrl) = self::getConfig();
        $ch = curl_init($baseUrl . "/v1/catalogs/products");
        curl_setopt_array($ch, [
            CURLOPT_HTTPHEADER => [
                "Authorization: Bearer $accessToken"
            ],
            CURLOPT_RETURNTRANSFER => true
        ]);
        $result = curl_exec($ch);
        curl_close($ch);
        return json_decode($result, true);
    }

    /**
     * 创建订阅计划
     * https://developer.paypal.com/docs/api/subscriptions/v1/#plans_create
     * @param $productId
     * @param $price
     * @param string $intervalUnit DAY | WEEK | MONTH | YEAR
     * @return mixed
     */
    static function createPlan($productId, $price = 0, $intervalUnit = 'DAY', $params = [])
    {
        $data = [
            "product_id" => $productId,
            "name" => $params['name'] ?? "Monthly Plan1111",
            "description" => $params['desc'] ?? "Monthly Subscription Plan",
            "billing_cycles" => [[
                "frequency" => [ // 此计费周期的频率详情。
                    "interval_unit" => $intervalUnit, // DAY | WEEK | MONTH | YEAR
                    "interval_count" => 1
                ],
                "tenure_type" => "REGULAR",  // REGULAR-固定的计费周期  TRIAL-一个试用计费周期
                "sequence" => 1,
                "total_cycles" => 0, // 无限循环
                "pricing_scheme" => [ //本次计费周期采用的是主动定价方案。而免费试用计费周期则无需设定定价方案。
                    "fixed_price" => [
                        "value" => $price,
                        "currency_code" => "USD"
                    ]
                ]
            ]],
            "payment_preferences" => [ // 订阅服务的付款方式。
                "auto_bill_outstanding" => true,
//                "setup_fee" => [ // 该项服务的初始设置费用
//                    "value" => $price,
//                    "currency_code" => "USD"
//                ],
//                //The action to take on the subscription if the initial payment for the setup fails.
//                "setup_fee_failure_action" => "CONTINUE", // CONTINUE | CANCEL  (初始设置费用的支付失败之后 的操作 -继续 | 取消)
                "payment_failure_threshold" => 3
            ]
        ];
        $accessToken = self::getToken();
        list($clientId, $clientSecret, $mode, $baseUrl) = self::getConfig();
        $ch = curl_init($baseUrl . "/v1/billing/plans");
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                "Authorization: Bearer $accessToken",
                "Content-Type: application/json",
                "Prefer: return=representation"
            ],
            CURLOPT_POSTFIELDS => json_encode($data)
        ]);
        $result = curl_exec($ch);
        curl_close($ch);
        return json_decode($result, true);
    }

开始订阅

 /**
     * 创建订阅
     * 官方文档地址:https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create
     * @param string $planId 计划ID
     * @param $data
     * @param $startTime
     * @return array
     */
    static function createSubscriptions($planId, $data = [], $startTime = null)
    {

        $locale = $data['locale'] ?? 'en-US';
        empty($startTime) && $startTime = date('c', time() + 5);
        $subscriptionsData = [
            "plan_id" => $planId,
            "start_time" => $startTime,
            "quantity" => "1",
            "auto_renewal" => 'true',
            "custom_id" => $data['custom_id'] ?? '',
            "application_context" => [
//                "brand_name" => "Your brand name",
                "locale" => empty($locale) ? 'en-US' : $locale,
//                "locale" => 'en-US',
                "shipping_preference" => "NO_SHIPPING",
                "user_action" => "SUBSCRIBE_NOW",
                "payment_method" => [
                    "payer_selected" => "PAYPAL",
                    "payee_preferred" => "IMMEDIATE_PAYMENT_REQUIRED"
                ],
                "return_url" => $data['return_url'] ?? '',
                "cancel_url" => $data['cancel_url'] ?? '',
            ]
        ];
        $accessToken = self::getToken();
        list($clientId, $clientSecret, $mode, $baseUrl) = self::getConfig();
        $ch1 = curl_init();
        curl_setopt($ch1, CURLOPT_URL, $baseUrl . "/v1/billing/subscriptions");
        curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch1, CURLOPT_POSTFIELDS, json_encode($subscriptionsData));
        curl_setopt($ch1, CURLOPT_POST, true);
        curl_setopt($ch1, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $accessToken,
            'Accept: application/json',
            'Content-Type: application/json'
        ]);
        $result = json_decode(curl_exec($ch1));
        curl_close($ch1);
        if (isset($result->error) && $result->error == 'invalid_token') {
            // token 失效时重新获取token
            self::getToken(true);
            return self::createSubscriptions($planId, $data, $startTime);
        }
        if (isset($result->debug_id) && $result->debug_id) {
            return ['code' => false, 'msg' => $result->details[0]->description];
        }
        return ['code' => true, 'url' => $result->links[0]->href, 'agreement_id' => $result->id];
    }

支付成功之后查询订阅(验证)


    /**
     * 查询用户订阅详情
     * 官方文档地址:https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get
     * @param string $subscriptionId agreement_id
     * @return mixed
     */
    static function getSubscriptionDetails($subscriptionId)
    {
        $accessToken = self::getToken();
        list($clientId, $clientSecret, $mode, $baseUrl) = self::getConfig();
        $ch1 = curl_init();
        curl_setopt($ch1, CURLOPT_URL, $baseUrl . "/v1/billing/subscriptions/" . $subscriptionId);
        curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch1, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $accessToken,
            'Accept: application/json',
            'Content-Type: application/json',
        ]);

        $result = json_decode(curl_exec($ch1));
        curl_close($ch1);
        if (isset($result->error) && $result->error == 'invalid_token') {
            // token 失效时重新获取token
            self::getToken(true);
            return self::getSubscriptionDetails($subscriptionId);
        }
        return json_decode(json_encode($result), true);
    }

取消订阅


    static function cancelSubscription($subscriptionId = '')
    {
        if (empty($subscriptionId)) {
            return '';
        }
        $accessToken = self::getToken();
        list($clientId, $clientSecret, $mode, $baseUrl) = self::getConfig();
        $ch1 = curl_init();
        curl_setopt($ch1, CURLOPT_URL, $baseUrl . "/v1/billing/subscriptions/{$subscriptionId}/cancel");
        curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
//        curl_setopt($ch1, CURLOPT_POSTFIELDS, json_encode());
        curl_setopt($ch1, CURLOPT_POST, true);
        curl_setopt($ch1, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $accessToken,
            'Accept: application/json',
            'Content-Type: application/json'
        ]);
        $result = json_decode(curl_exec($ch1), true);
        curl_close($ch1);
        return json_decode(json_encode($result), true);
    }

回调

 // 回调通知
    function payPalNotice()
    {
        $requestBody = file_get_contents("php://input");
        $params = json_decode($requestBody, true);
        $headers = getallheaders();
        $headers = array_change_key_case($headers, CASE_UPPER);
        $lock = new IRedisLock('paypal_notify_' . ($params['id'] ?? ''));
        if (!$lock->islock()) {
            http_response_code(200);
            return json(['msg' => 'Success', 'code' => 'SUCCESS']);
        }
        LogHelperUtil::outLog('Paypal', ['params' => $params, 'headers' => $headers], 'notify/paypal');

        $event_type = $params['event_type'] ?? ''; // 事件类型
        $resource_type = $params['resource_type'] ?? ''; // 资源类型
        $resource = $params['resource'] ?? []; // 资源
        $tradeNo = $paymentId = $resource['parent_payment'] ?? '';
        $custom = stripslashes($resource['custom'] ?? '{}');
        $custom = json_decode(trim($custom, '"'), true); // 发起支付的定制数据
        $payPlatform = $custom['pay_platform'] ?? '';
        $orderSn = $custom['order_sn'] ?? '';
        $uid = intval($custom['uid'] ?? 0);
        if ($event_type == 'PAYMENT.SALE.COMPLETED' && $resource_type == 'sale') {
            $billingAgreementId = $resource['billing_agreement_id'] ?? 0; // 有值说明是订阅续费
            // 支付成功
        }
     


        if ($resource_type == 'subscription') {
            //  BILLING.SUBSCRIPTION.CREATED	用户创建订阅
            //  BILLING.SUBSCRIPTION.ACTIVATED	订阅已生效
            //  PAYMENT.SALE.COMPLETED	订阅续费成功  $resource_type == 'sale'
            //  BILLING.SUBSCRIPTION.CANCELLED	用户取消订阅
            if ($event_type == 'BILLING.SUBSCRIPTION.CREATED' || $event_type == 'BILLING.SUBSCRIPTION.ACTIVATED' || $event_type == 'BILLING.SUBSCRIPTION.CANCELLED') {
                LogHelperUtil::outLog("订阅:", json_encode($params), 'notify/paypal_sub');
                return json(['msg' => 'Success', 'code' => 'SUCCESS']);
            }
        }

        http_response_code(200);
        return json(['msg' => 'Success', 'code' => 'SUCCESS']);
    }

初始化的函数

    static function getConfig()
    {
        // 下面为申请app获得的clientId和clientSecret,必填项,否则无法生成token。
        // 沙盒模式
//        $config = SystemService::getConfigByType('pay_account_config');
//        $config = $config['paypal_config'] ?? []; // 获取Paypal配置
        $config = config('app.paypal');
        $mode = $config['mode'] ?? '';  // sandbox-沙箱模式 live-线上
        $clientId = $config['clientId'] ?? '';
        $clientSecret = $config['clientSecret'] ?? '';
        if ($mode == 'live') {
            $baseUrl = 'https://api.paypal.com';
        } else {
            $baseUrl = 'https://api-m.sandbox.paypal.com';
        }
        return [$clientId, $clientSecret, $mode, $baseUrl];
    }
 /**
     * 获取token
     * 官方文档地址:https://developer.paypal.com/api/rest/authentication/
     * @param $is_expires
     * @return false|mixed|\Redis|string
     */
    static function getToken($is_expires = false)
    {
        static $statusToken = null;
        if ($statusToken && !$is_expires) {
            return $statusToken;
        }

        $redis = IRedis::redisCache();
        $key = 'paypal_token';
        $token = $redis->get($key);
        if ($token && !$is_expires) {
            $statusToken = $token;
            return $token;
        }
        list($clientId, $clientSecret, $mode, $baseUrl) = self::getConfig();
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $baseUrl . "/v1/oauth2/token");
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, $clientId . ':' . $clientSecret);
        curl_setopt($ch, CURLOPT_POSTFIELDS, "grant_type=client_credentials");
        $result = curl_exec($ch);
        if (empty($result)) {
            curl_close($ch);
            return false;
        } else {
            curl_close($ch);
            $result = json_decode($result);
            $token = $result->access_token ?? '';
            if (!empty($token)) {
                $redis->set($key, $token);
                $redis->expireAt($key, $result->expires_in - 300);
            }
            $statusToken = $token;
            return $token;
        }
    }
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐