php 实现paypal订阅

paypal订阅与google订阅的不同之处:

1. google设置首周优惠时,用户第一次订阅会有优惠,用户退订之后再次订阅就不会执行首周订阅优惠; 而paypal设置首周优惠时,用户的每次取消后订阅都视为新订阅,都满足首周优惠条件

2. paypal订阅成功回调时不会返回 “订阅到期的时间”

订阅流程及创建计划查看:https://blog.csdn.net/weixin_39461487/article/details/125900163

1、后台创建订阅计划

正式环境创建订阅计划地址:

PayPal Subscription | Automate repeat payments | PayPal US

沙盒环境创建订阅计划计划:

  • 对于您的一个沙盒业务帐户-->https://www.sandbox.paypal.com/billing/plans
  • 对于您的live帐户-->https://www.paypal.com/billing/plans

2、curl请求并订阅计划

 2.1 初始化参数

class PaypalService extends Service
{
    public $baseUrl;
    public $paypalParams;
    public $access_token;

    public function __construct($config = [])
    {
        parent::__construct();
        $this->paypalParams = [
            'return_url' => '支付成功跳转地址',
            'cancel_url' => '支付失败跳转地址',
            'client_id' => '',
            'client_secret' => '',
        ];
        if (!'test') {
            $this->baseUrl = 'https://api.paypal.com';
        } else {
            $this->baseUrl = 'https://api.sandbox.paypal.com';
        }
    }
}

2.2 获取token

官方文档地址:https://developer.paypal.com/api/rest/authentication/

public function getToken($is_expires=false)
    {
        $redis = new RedisStore();
        $key = 'paypal_token';
        $token = $redis->get($key);
        if($token && !$is_expires) {
            $this->access_token = $token;
            return true;
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,  $this->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, $this->paypalParams['client_id'] . ':' . $this->paypalParams['client_secret']);
        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);
            $redis->set($key, $result->access_token);
            $redis->expireAt($key, $result->expires_in - 300);
            $this->access_token = $result->access_token;
            return true;
        }
    }

2.3 创建订阅

官方文档地址:https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create

public function createSubscriptions($subscriptions_plan_id, $start_time = null, $is_expires=false)
    {
        if(!$this->access_token) $this->getToken();
        if ($start_time === null) $start_time = date('c', time()+5);
        $subscriptionsData = [
            "plan_id" => $subscriptions_plan_id,
            "start_time" => $start_time,
            "quantity" => "1",
            "auto_renewal" => 'true',
            "application_context" => [
//                "brand_name" => "Your brand name",
                "locale" => "en-US",
                "shipping_preference" => "NO_SHIPPING",
                "user_action" => "SUBSCRIBE_NOW",
                "payment_method" => [
                    "payer_selected" => "PAYPAL",
                    "payee_preferred" => "IMMEDIATE_PAYMENT_REQUIRED"
                ],
                "return_url" => $this->paypalParams['return_url'],
                "cancel_url" => $this->paypalParams['cancel_url'],
            ]
        ];

        $ch1 = curl_init();
        curl_setopt($ch1, CURLOPT_URL, $this->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 ' . $this->access_token,
            'Accept: application/json',
            'Content-Type: application/json'
        ]);

        $result = json_decode(curl_exec($ch1));
        curl_close($ch1);
        if($result->error == 'invalid_token') {
            // token 失效时重新获取token
            $this->getToken(true);
            return $this->createSubscriptions($subscriptions_plan_id, $start_time, true);
        }
        if($result->debug_id) {
            return ['code' => false, 'msg' => $result->details[0]->description];
        }
        return ['code' => true, 'url' => $result->links[0]->href, 'agreement_id' => $result->id];
    }

2.4 查询用户订阅详情

官方文档地址:https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get

public function getSubscriptionDetails($subscription_id, $is_expires=false)
    {
        if(!$this->access_token) $this->getToken();
        $ch1 = curl_init();
        curl_setopt($ch1, CURLOPT_URL, $this->baseUrl . "/v1/billing/subscriptions/" . $subscription_id);
        curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch1, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $this->access_token,
            'Accept: application/json',
            'Content-Type: application/json',
        ]);

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

2.5 封装完整代码

class PaypalService extends Service
{
    public $baseUrl;
    public $paypalParams;
    public $access_token;

    public function __construct($config = [])
    {
        parent::__construct();
        $this->paypalParams = [
            'return_url' => '支付成功跳转地址',
            'cancel_url' => '支付失败跳转地址',
            'client_id' => '',
            'client_secret' => '',
        ];
        if (!'test') {
            $this->baseUrl = 'https://api.paypal.com';
        } else {
            $this->baseUrl = 'https://api.sandbox.paypal.com';
        }
    }

    /**
     * 获取token
     * @param false $is_expires 是否强制更新token
     * https://developer.paypal.com/api/rest/authentication/
     * @return false|mixed
     */
    public function getToken($is_expires=false)
    {
        $redis = new RedisStore();
        $key = 'paypal_token';
        $token = $redis->get($key);
        if($token && !$is_expires) {
            $this->access_token = $token;
            return true;
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,  $this->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, $this->paypalParams['client_id'] . ':' . $this->paypalParams['client_secret']);
        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);
            $redis->set($key, $result->access_token);
            $redis->expireAt($key, $result->expires_in - 300);
            $this->access_token = $result->access_token;
            return true;
        }
    }

    /**
     * 创建订阅
     * @param $subscriptions_plan_id
     * @param null $start_time
     * @param false $is_expires 是否强制更新token
     * https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create
     * @return array
     */
    public function createSubscriptions($subscriptions_plan_id, $start_time = null, $is_expires=false)
    {
        if(!$this->access_token) $this->getToken();
        if ($start_time === null) $start_time = date('c', time()+5);
        $subscriptionsData = [
            "plan_id" => $subscriptions_plan_id,
            "start_time" => $start_time,
            "quantity" => "1",
            "auto_renewal" => 'true',
            "application_context" => [
//                "brand_name" => "Your brand name",
                "locale" => "en-US",
                "shipping_preference" => "NO_SHIPPING",
                "user_action" => "SUBSCRIBE_NOW",
                "payment_method" => [
                    "payer_selected" => "PAYPAL",
                    "payee_preferred" => "IMMEDIATE_PAYMENT_REQUIRED"
                ],
                "return_url" => $this->paypalParams['return_url'],
                "cancel_url" => $this->paypalParams['cancel_url'],
            ]
        ];

        $ch1 = curl_init();
        curl_setopt($ch1, CURLOPT_URL, $this->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 ' . $this->access_token,
            'Accept: application/json',
            'Content-Type: application/json'
        ]);

        $result = json_decode(curl_exec($ch1));
        curl_close($ch1);
        if($result->error == 'invalid_token') {
            // token 失效时重新获取token
            $this->getToken(true);
            return $this->createSubscriptions($subscriptions_plan_id, $start_time, true);
        }
        if($result->debug_id) {
            return ['code' => false, 'msg' => $result->details[0]->description];
        }
        return ['code' => true, 'url' => $result->links[0]->href, 'agreement_id' => $result->id];
    }

    /**
     * 获取订阅计划详情
     * @param $subscription_id
     * @param false $is_expires 是否强制获取token
     * https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get
     * @return mixed
     */
    public function getSubscriptionDetails($subscription_id, $is_expires=false)
    {
        if(!$this->access_token) $this->getToken();
        $ch1 = curl_init();
        curl_setopt($ch1, CURLOPT_URL, $this->baseUrl . "/v1/billing/subscriptions/" . $subscription_id);
        curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch1, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $this->access_token,
            'Accept: application/json',
            'Content-Type: application/json',
        ]);

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

3、订阅支付

$subscription = $this->createSubscriptions('计划id');
if($subscription['code'] === false) {
    return [
        'code' => ErrorCode::EC_UNKNOWN,
        'msg' => 'Paypal Subscriptions Error: ' . $subscription['msg'],
        'data' => [],
    ];
}

// 购买商品下单流程...
// 成功返回的结果
// 'url' => $subscription['url'],
// 'agreement_id' => $subscription['agreement_id'],

返回结果:

id:paypal生成的订阅ID

links[0]['href']:去支付的链接

object(stdClass)#189 (4) {
  ["status"]=>
  string(16) "APPROVAL_PENDING"
  ["id"]=>
  string(14) "I-BU64KX8FTG3C"
  ["create_time"]=>
  string(20) "2022-08-09T05:32:29Z"
  ["links"]=>
  array(3) {
    [0]=>
    object(stdClass)#219 (3) {
      ["href"]=>
      string(90) "https://www.sandbox.paypal.com/webapps/billing/subscriptions?ba_token=BA-3P5459826W8205613"
      ["rel"]=>
      string(7) "approve"
      ["method"]=>
      string(3) "GET"
    }
    [1]=>
    object(stdClass)#220 (3) {
      ["href"]=>
      string(70) "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-BU64KX8FTG3C"
      ["rel"]=>
      string(4) "edit"
      ["method"]=>
      string(5) "PATCH"
    }
    [2]=>
    object(stdClass)#221 (3) {
      ["href"]=>
      string(70) "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-BU64KX8FTG3C"
      ["rel"]=>
      string(4) "self"
      ["method"]=>
      string(3) "GET"
    }
  }
}

4、订阅回调

回调需要配置webhook及回调地址

 事件说明:Webhook event names

public function actionPaypal()
{
    $payload = @file_get_contents('php://input');
    $content = "=========".date('Y-m-d H:i:s',time())."==========\r\n";
    file_put_contents('paypal_success.log', $content . $payload . "\r\n",FILE_APPEND);
    $content = json_decode($payload);
}

首次订阅成功会回调两个事件:

1.BILLING.SUBSCRIPTION.ACTIVATED

=========2022-08-02 05:58:58==========
{"id":"WH-2JK13221SH228131A-85M888883CF992091V","event_version":"1.0","create_time":"2022-08-02T09:58:37.896Z","resource_type":"subscription","resource_version":"2.0","event_type":"BILLING.SUBSCRIPTION.ACTIVATED","summary":"Subscription activated","resource":{"quantity":"1","subscriber":{"email_address":"wwww@personal.example.com","payer_id":"BNMR2YZQJ9Q2L","name":{"given_name":"John","surname":"Doe"}},"create_time":"2022-08-02T09:58:05Z","plan_overridden":false,"shipping_amount":{"currency_code":"USD","value":"0.0"},"start_time":"2022-08-02T09:57:17Z","update_time":"2022-08-02T09:58:06Z","billing_info":{"outstanding_balance":{"currency_code":"USD","value":"0.0"},"cycle_executions":[{"tenure_type":"TRIAL","sequence":1,"cycles_completed":1,"cycles_remaining":0,"current_pricing_scheme_version":1,"total_cycles":1},{"tenure_type":"REGULAR","sequence":2,"cycles_completed":0,"cycles_remaining":0,"current_pricing_scheme_version":1,"total_cycles":0}],"last_payment":{"amount":{"currency_code":"USD","value":"0.99"},"time":"2022-08-02T09:58:05Z"},"next_billing_time":"2022-08-03T10:00:00Z","failed_payments_count":0},"links":[{"href":"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N/cancel","rel":"cancel","method":"POST","encType":"application/json"},{"href":"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N","rel":"edit","method":"PATCH","encType":"application/json"},{"href":"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N","rel":"self","method":"GET","encType":"application/json"},{"href":"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N/suspend","rel":"suspend","method":"POST","encType":"application/json"},{"href":"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N/capture","rel":"capture","method":"POST","encType":"application/json"}],"id":"I-DL5L53KM3N","plan_id":"P-4G238388SD824MLUPJNY","status":"ACTIVE","status_update_time":"2022-08-02T09:58:06Z"},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2JK13221SH228131A-85M80963CF992091V","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2JK13221SH228131A-85M80963CF992091V/resend","rel":"resend","method":"POST"}]}

2.  PAYMENT.SALE.COMPLETED

=========2022-08-02 05:58:58==========
{"id":"WH-4AU27166UL181633V-3BU78225VU943751T","event_version":"1.0","create_time":"2022-08-02T09:58:43.670Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 0.99 USD","resource":{"billing_agreement_id":"I-DL5L53KM3N","amount":{"total":"0.99","currency":"USD","details":{"subtotal":"0.99"}},"payment_mode":"INSTANT_TRANSFER","update_time":"2022-08-02T09:58:05Z","create_time":"2022-08-02T09:58:05Z","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","transaction_fee":{"currency":"USD","value":"0.33"},"protection_eligibility":"ELIGIBLE","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v1/payments/sale/4DN9460xxx61205Y"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/4DN9460xxx61205Y/refund"}],"id":"4DN9460xxx61205Y","state":"completed","invoice_number":""},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4AU27166UL181633V-3BU78225VU943751T","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4AU27166UL181633V-3BU78225VU943751T/resend","rel":"resend","method":"POST"}]}

续订时只会回调:PAYMENT.SALE.COMPLETED

=========2022-08-03 06:25:06==========
{"id":"WH-4AU27166UL181633V-3BU78225VU943751T","event_version":"1.0","create_time":"2022-08-02T09:58:43.670Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 0.99 USD","resource":{"billing_agreement_id":"I-DL5L53KM3N","amount":{"total":"0.99","currency":"USD","details":{"subtotal":"0.99"}},"payment_mode":"INSTANT_TRANSFER","update_time":"2022-08-02T09:58:05Z","create_time":"2022-08-02T09:58:05Z","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","transaction_fee":{"currency":"USD","value":"0.33"},"protection_eligibility":"ELIGIBLE","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v1/payments/sale/4DN9460xxx61205Y"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/4DN9460xxx61205Y/refund"}],"id":"4DN9460xxx61205Y","state":"completed","invoice_number":""},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4AU27166UL181633V-3BU78225VU943751T","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4AU27166UL181633V-3BU78225VU943751T/resend","rel":"resend","method":"POST"}]}

4.1 订阅创建回调

事件:BILLING.SUBSCRIPTION.ACTIVATED

if($content->resource_type == 'subscription') {
    // 新建订阅
    if($content->event_type == 'BILLING.SUBSCRIPTION.ACTIVATED') {
        // 验证订单信息
        $agreement_id = $content->resource->id;
        $goodId = $content->resource->plan_id;
        // 支付价格
        $price = $content->resource->billing_info->last_payment->amount->value;
        // 创建时间
        $start_time = strtotime(str_replace('T', ' ', trim($content->resource->create_time, 'Z')));
        // 状态更新时间
        $update_time = strtotime(str_replace('T', ' ', trim($content->resource->update_time, 'Z')));
        
        // todo 验证并记录用户订阅时间...
    }
}

4.2 支付成功或续订成功回调

事件:PAYMENT.SALE.COMPLETED

因订阅回调与支付成功回调不知道哪个先后,所以此处订单表内记录了两个订阅的字段,agreement_id:创建订阅时返回的id,out_trade_no:订单编号

如果此支付成功回调与订阅回调时间小于1小时,证明此支付成功回调与订阅回调为同一订单,我再处理默认首次订阅处理回调走BILLING.SUBSCRIPTION.ACTIVATED

if($content->resource_type == 'sale') {
    if($content->event_type == 'PAYMENT.SALE.COMPLETED') {
        $agreement_id = $content->resource->billing_agreement_id; // 若是订阅,此字段为同意协议id,
        $payment_intent_new = $content->resource->id; // 订单id
        $update_time = strtotime(str_replace('T', ' ', trim($content->resource->update_time, 'Z')));
        // 支付价格
        $price = $content->resource->amount->total;
        if($agreement_id) {
            // 因订阅回调与支付成功回调不知道哪个先后,根据agreement_id查询首订阅订单,如果存在后续验证,不存在说明此回调在前,订阅成功回调在后,首订阅默认执行订阅成功回调
            $objTradeInfoOld = VipBuy::find()->andWhere(['agreement_id' => $agreement_id, 'status' => VipBuy::STATUS_SUCCESS])->one();
            if(objTradeInfoOld) {
                if($update_time - $objTradeInfoOld->notify_time <= 3600) {
                    // 如果此支付成功回调与订阅回调时间小于1小时,证明此支付成功回调与订阅回调为同一订单
                    return true;
                }
                $objTradeInfo = VipBuy::find()->andWhere(['out_trade_no' => $payment_intent_new, 'goods_id' => $objTradeInfoOld->goods_id, 'product' => $product])->one();
                if($objTradeInfo->status == VipBuy::STATUS_SUCCESS) return true;
                if(!$objTradeInfo) {
                    // 此为续订订单,生成新订单并执行其他逻辑
                }
            }
        }
    }
}

4.3 取消订阅回调

取消订阅流程:

事件:BILLING.SUBSCRIPTION.CANCELLED

if($content->resource_type == 'subscription') {
	// 取消订阅
    if($content->event_type == 'BILLING.SUBSCRIPTION.CANCELLED' && $content->resource->status == 'CANCELLED') {
        $payment_intent = $content->resource->id;
        $goodId = $content->resource->plan_id;
        // 状态更新时间,可记录为用户取消订阅时间
        $update_time = strtotime(str_replace('T', ' ', trim($content->resource->status_update_time, 'Z')));
        // todo 取消订阅后续操作...
    }
}

订阅完成

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在Android应用中接入PayPal订阅功能,可以按照以下步骤进行: 1. 集成PayPal SDK:首先,你需要在你的Android应用中集成PayPal SDK。你可以从PayPal开发者网站获取最新的SDK,并按照文档进行集成。 2. 创建PayPal订阅计划:在PayPal开发者控制台上创建一个订阅计划。你需要提供相关的订阅信息,如价格、订阅周期、订阅名称等。 3. 获取客户端ID:在PayPal开发者控制台上创建一个应用,然后获取一个客户端ID。这个客户端ID将用于在Android应用中进行身份验证和支付操作。 4. 在Android应用中配置支付信息:在你的Android应用中配置PayPal支付信息,包括客户端ID和其他相关设置。这将确保你的应用能够与PayPal进行通信。 5. 调起PayPal支付界面:当用户选择订阅购买某项服务时,你可以通过调用PayPal SDK提供的接口,传递订阅信息,调起PayPal支付界面供用户进行支付操作。用户可以使用PayPal账号或信用卡完成支付过程。 6. 处理支付结果回调:用户完成支付后,PayPal会将支付结果返回给你的Android应用。你需要在应用中接收到回调通知后,验证支付结果的真实性,并更新用户的订阅状态或购买状态。 7. 自动续订处理:PayPal会自动处理订阅的续订操作,你无需手动处理。当订阅周期即将结束时,PayPal会自动向用户收取续订费用,并发送回调通知给你的应用。 需要注意的是,接入PayPal订阅功能需要你在服务器端进行管理和处理,通过与PayPal的接口交互实现相关功能。同时,你需要遵守PayPal的相关规定和合规要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值