李帅

1.使用谷歌服务账号进行验证

......@@ -3,6 +3,7 @@
namespace App\Console\Commands;
use App\Payment\GooglePayment;
use Carbon\Carbon;
use Illuminate\Console\Command;
class DevGoogle extends Command
......@@ -38,21 +39,27 @@ class DevGoogle extends Command
*/
public function handle()
{
//
// $credentials_file = public_path().'/pc-api-7482901338487549764-603-566eccf76b91.json';
// $client = new \Google_Client();
// $client->setAuthConfig($credentials_file);
// $client->setApplicationName("Client_Library_Examples");
// $client->setScopes(
// [
// \Google_Service_AndroidPublisher::ANDROIDPUBLISHER,
// ]
// );
//
// $validator =new \Google_Service_AndroidPublisher($client);
// $resp = $validator->purchases_subscriptions->get('pub.yiyan.parlando.Parlando', 'monthly_yiyan_vip', 'aa');
// dd($resp);
$credentials_file = public_path().'/pc-api-7482901338487549764-603-566eccf76b91.json';
$client = new \Google_Client();
$client->setAuthConfig($credentials_file);
$client->setApplicationName("Client_Library_Examples");
$client->setScopes(
[
\Google_Service_AndroidPublisher::ANDROIDPUBLISHER,
]
);
$validator =new \Google_Service_AndroidPublisher($client);
$resp = $validator->purchases_subscriptions->get('pub.yiyan.parlando.Parlando', 'monthly_yiyan_vip', 'aa');
dd($resp);
$time = 1668186646527;
$startTime = 1668184557316;
echo date('Y-m-d H:i:s',$startTime/1000) . PHP_EOL;
echo date('Y-m-d H:i:s',$time/1000) . PHP_EOL;
echo Carbon::createFromTimestampMs($time)->toDateTimeString();
return 0;
}
}
......
......@@ -25,6 +25,12 @@ class Controller extends BaseController
{
$ua = request()->header('user-agent');
return 'ios';
if (strpos($ua, 'iPhone') || strpos($ua, 'iPad') || strpos($ua,'Mac OS X')) {
return 'ios';
}else if (strpos($ua, 'Android') || strpos($ua, 'android')){
return 'android';
}else{
return 'unkown';
}
}
}
......
......@@ -23,6 +23,23 @@ class GooglePayment implements PaymentInterface
const IS_SANDBOX = true;
const SUBSCRIPTION_RECOVERED = 1; //- 从帐号保留状态恢复了订阅。
const SUBSCRIPTION_RENEWED = 2; // 续订了处于活动状态的订阅。
const SUBSCRIPTION_CANCELED = 3; // 自愿或非自愿地取消了订阅。如果是自愿取消,在用户取消时发送。
const SUBSCRIPTION_PURCHASED = 4; // 购买了新的订阅。
const SUBSCRIPTION_ON_HOLD = 5; // 订阅已进入帐号保留状态(如果已启用)。
const SUBSCRIPTION_IN_GRACE_PERIOD = 6; // 订阅已进入宽限期(如果已启用)。
const SUBSCRIPTION_RESTARTED = 7; // 用户已通过 Play > 帐号 > 订阅重新激活其订阅(需要选择使用订阅恢复功能)。
const SUBSCRIPTION_PRICE_CHANGE_CONFIRMED = 8; // 用户已成功确认订阅价格变动。
const SUBSCRIPTION_DEFERRED = 9; // 订阅的续订时间点已延期。
const SUBSCRIPTION_PAUSED = 10; // 订阅已暂停。
const SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED = 11; // 订阅暂停计划已更改。
const SUBSCRIPTION_REVOKED = 12; // 用户在到期时间之前已撤消订阅。
const SUBSCRIPTION_EXPIRED = 13; // 订阅已到期。
const ONE_TIME_PRODUCT_PURCHASED = 1; //用户成功购买了一次性商品。
const ONE_TIME_PRODUCT_CANCELED = 2; // 用户已取消待处理的一次性商品购买交易。
public $client;
public function __construct()
......@@ -30,8 +47,6 @@ class GooglePayment implements PaymentInterface
$this->client = $this->getGoogleClient();
}
public function prepare(Order $order)
{
// 查询订单对应的产品id
......@@ -143,100 +158,125 @@ class GooglePayment implements PaymentInterface
public function notify($string)
{
//subscriptionNotification(订阅相关)、oneTimeProductNotification(一次性购买相关)testNotification(测试发布相关) 三个不会同时存在任意2个
//subscriptionNotification说明
//参数名 说明
//version 此通知的版本。最初,此值为“1.0”。此版本与其他版本字段不同。
//notificationType int 订阅的 notificationType 可以参考下面的表
//purchaseToken 购买订阅时向用户设备提供的令牌
//subscriptionId 所购买订阅的 ID(例如“monthly001”)
//SUBSCRIPTION_RECOVERED - 从帐号保留状态恢复了订阅。
//SUBSCRIPTION_RENEWED - 续订了处于活动状态的订阅。
//SUBSCRIPTION_CANCELED - 自愿或非自愿地取消了订阅。如果是自愿取消,在用户取消时发送。
//SUBSCRIPTION_PURCHASED - 购买了新的订阅。
//SUBSCRIPTION_ON_HOLD - 订阅已进入帐号保留状态(如果已启用)。
//SUBSCRIPTION_IN_GRACE_PERIOD - 订阅已进入宽限期(如果已启用)。
//SUBSCRIPTION_RESTARTED - 用户已通过 Play > 帐号 > 订阅重新激活其订阅(需要选择使用订阅恢复功能)。
//SUBSCRIPTION_PRICE_CHANGE_CONFIRMED - 用户已成功确认订阅价格变动。
//SUBSCRIPTION_DEFERRED - 订阅的续订时间点已延期。
//SUBSCRIPTION_PAUSED - 订阅已暂停。
//SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED - 订阅暂停计划已更改。
//SUBSCRIPTION_REVOKED - 用户在到期时间之前已撤消订阅。
//SUBSCRIPTION_EXPIRED - 订阅已到期。
// 'acknowledgementState' => 1,
// 'autoRenewing' => false, // 设置是否在订阅到期时自动续订。
// 'autoResumeTimeMillis' => NULL,
// 'cancelReason' => 1, //订阅被取消或未自动续订的原因。可能的值包括:0.用户取消了订阅 1.由于结算问题 2.系统取消了订阅 2. 订阅被新的订阅取代了。3.开发者已取消订阅
// 'cancelSurveyResultType' => 'Google\\Service\\AndroidPublisher\\SubscriptionCancelSurveyResult',
// 'cancelSurveyResultDataType' => '',
// 'countryCode' => 'CA',
// 'developerPayload' => '',
// 'emailAddress' => NULL,
// 'expiryTimeMillis' => '1668023054641', // 订阅的到期时间
// 'externalAccountId' => NULL,
// 'familyName' => NULL,
// 'givenName' => NULL,
// 'introductoryPriceInfoType' => 'Google\\Service\\AndroidPublisher\\IntroductoryPriceInfo',
// 'introductoryPriceInfoDataType' => '',
// 'kind' => 'androidpublisher#subscriptionPurchase',
// 'linkedPurchaseToken' => NULL,
// 'obfuscatedExternalAccountId' => 'osnB10209503106007', //我们系统的订单号
// 'obfuscatedExternalProfileId' => NULL,
// 'orderId' => 'GPA.3390-9999-6519-16223..5',
// 'paymentState' => NULL, //订阅的付款状态。可能的值包括:0=待处理付款,1=付款已收讫,2=免费试用,3=延迟升级/降级.已取消、已过期的订阅不会显示。
// 'priceAmountMicros' => '5490000', // 订阅价格。例如,如果订阅价格为 1.99 欧元,priceAmountMicros 为 1990000。
// 'priceChangeType' => 'Google\\Service\\AndroidPublisher\\SubscriptionPriceChange',
// 'priceChangeDataType' => '',
// 'priceCurrencyCode' => 'CAD',
// 'profileId' => NULL,
// 'profileName' => NULL,
// 'promotionCode' => NULL,
// 'promotionType' => NULL,
// 'purchaseType' => 0, // 订阅的购买类型 0=测试,1=促销,当购买交易不是使用标准的应用内购买结算流程时,系统才会设置此字段。
// 'startTimeMillis' => '1668020958362',
// 'userCancellationTimeMillis' => NULL,
// 'internal_gapi_mappings' =>
$data = json_decode(base64_decode($string),true);
Log::debug('返回的数据:====================');
Log::debug(print_r($data,true));
$packageName = $data['packageName'];
if (isset($data['subscriptionNotification'])) {
$subscriptionId = $data['subscriptionNotification']['subscriptionId'];
$purchaseToken = $data['subscriptionNotification']['purchaseToken'];
try{
$validator =new \Google_Service_AndroidPublisher($this->client);
$resp = $validator->purchases_subscriptions->get($packageName, $subscriptionId, $purchaseToken);
}catch (\Exception $exception){
Log::error("查询订阅出错了:==================");
Log::error("packageName:".$packageName);
Log::error("subscriptionId:". $subscriptionId);
Log::error("purchaseToken:".$purchaseToken);
Log::error("===============================");
return false;
}
$orderSn = $resp->getObfuscatedExternalAccountId();
$order = Order::query()->where('order_sn',$orderSn)->first();
if (!$order) {
Log::error("查无此订单");
return false;
}
switch ($data['notificationType']){
case self::SUBSCRIPTION_PURCHASED:
if ($resp->getPaymentState() == 1) $order->status = Order::DONE;
/** 修改订单状态*/
$order->pay_time = Carbon::now();
$order->pay_number = $resp->getOrderId();
$order->pay_type = $resp->getKind();
$order->callback = $string;
$order->save();
/** 给用户加会员*/
$user = UserProfile::query()->find($order->user_id);
$user->is_vip = 1;
$user->create_vip_time = Carbon::createFromTimestampMs($resp->getStartTimeMillis());
$user->expire_vip_time = Carbon::createFromTimestampMs($resp->getExpiryTimeMillis());
$user->buy_number += 1;
$user->buy_amount += $order->pay_amount;
$user->last_buy_time = Carbon::now();
$user->save();
break;
case self::SUBSCRIPTION_RENEWED :
case self::SUBSCRIPTION_RESTARTED:
/** 给用户加会员*/
$user = UserProfile::query()->find($order->user_id);
$user->is_vip = 1;
$user->expire_vip_time = Carbon::createFromTimestampMs($resp->getExpiryTimeMillis());
$user->buy_number += 1;
$user->buy_amount += $order->pay_amount;
$user->last_buy_time = Carbon::now();
$user->save();
break;
case self::SUBSCRIPTION_CANCELED:
case self::SUBSCRIPTION_EXPIRED:
/** 给用户取消会员*/
$user = UserProfile::query()->find($order->user_id);
$user->is_vip = 0;
$user->expire_vip_time = Carbon::createFromTimestampMs($resp->getExpiryTimeMillis());
$user->save();
break;
default:
Log::debug('特殊通知类型:'.$data['notificationType'].' ====================');
Log::debug(var_export($resp,true));
}
}elseif (isset($data['subscriptionNotification'])){
// oneTimeProductNotification 一次性购买相关
/**
* 成功会返回返回字段解释 https://developers.google.com/android-publisher/api-ref/purchases/products
* 返回字段解释 中文翻译
* consumptionState int 消费类产品的消费状态 0有待消费1已消耗
* developerPayload string onyx系统生成的唯一ID
* kind sgring 购买的对象
* orderId sgring 客户支付订单ID(google play 订单ID)
* purchaseState int 订单的采购状态 0购买1取消
* purchaseTimeMillis int 时间戳
* purchaseType int 扩展字段 0 测试 1促销
* Array(
* [consumptionState] => 1
* [developerPayload] => 你的订单号
* [kind] => androidpublisher#productPurchase
* [orderId] => google play 订单号
* [purchaseState] => 0
* [purchaseTimeMillis] => 1542187625018
* [purchaseType] =>
* )
*/
//1.3. 根据解密后的内容去google接口做查询校验并发货(关键参数expiryTimeMillis)
//根据解密后的内容去google接口做校验
//Google文档地址
//请求域名(Get/form):
//https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}
$validator =new \Google_Service_AndroidPublisher($this->client);
$resp = $validator->purchases_subscriptions->get($packageName, $subscriptionId, $purchaseToken);
Log::debug('新订阅人员:====================');
Log::debug(var_export($resp,true));
Log::debug("购买状态" . $resp->getAcknowledgementState()); //0。待确认 1. 已确认
//1.4. 消耗该笔订单
//Google文档地址
//请求域名(Post/form)
//https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}:acknowledge
// $acknowledge = new SubscriptionPurchasesAcknowledgeRequest();
// $acknowledge->developerPayload = ''; // todo 可以将我们系统的订单号、用户支付金额等存入谷歌订单里
// $resp = $validator->purchases_subscriptions->acknowledge($packageName, $subscriptionId, $purchaseToken,$acknowledge);
//
// Log::debug('消耗结果:====================');
// Log::debug(var_export($resp,true));
// todo 发货
$productId = $data['OneTimeProductNotification']['purchaseToken'];
$purchaseToken = $data['OneTimeProductNotification']['purchaseToken'];
try {
$validator =new \Google_Service_AndroidPublisher($this->client);
$resp = $validator->purchases_products->get($packageName, $productId, $purchaseToken);
} catch (\Exception $e) {
Log::error("查询订阅出错了:==================");
Log::error("packageName:".$packageName);
Log::error("productId:". $productId);
Log::error("purchaseToken:".$purchaseToken);
Log::error("===============================");
return false;
}
// ...
switch ($data['notificationType']){
case self::ONE_TIME_PRODUCT_PURCHASED :
case self::ONE_TIME_PRODUCT_CANCELED :
default:
Log::debug('特殊通知类型:'.$data['notificationType'].' ====================');
Log::debug(var_export($resp,true));
break;
}
}else{
Log::debug("收到测试通知信息 version:" . $data['testNotification']['version']);
}
}
......