Php 开发Paypal订阅支付功能
·
创建产品和订阅计划
产品 和计划是可以共用的,不需要每个用户单独创建
后台添加产品和计划
正式: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;
}
}
更多推荐


所有评论(0)