From 153c9262fe512fe3494f18f2178d954329539a71 Mon Sep 17 00:00:00 2001 From: azw Date: Sun, 30 Jul 2023 15:36:11 +0800 Subject: [PATCH] 1 --- webapp/models/InAppOrder.php | 4 + webapp/models/OutAppOrder.php | 30 ++ webapp/services/callback/InAppPurchase.php | 331 +++++++++----------- webapp/services/callback/OutAppPurchase.php | 72 +++-- 4 files changed, 228 insertions(+), 209 deletions(-) diff --git a/webapp/models/InAppOrder.php b/webapp/models/InAppOrder.php index 2bc16bbc..6ed6b0ba 100644 --- a/webapp/models/InAppOrder.php +++ b/webapp/models/InAppOrder.php @@ -7,6 +7,10 @@ use phpcommon\SqlHelper; class InAppOrder extends BaseModel { + const PENDING_STATE = 0; + const FINISHED_STATE = 1; + const FAILED_STATE = 2; + public static function find($orderId) { $row = SqlHelper::ormSelectOne( myself()->_get(), diff --git a/webapp/models/OutAppOrder.php b/webapp/models/OutAppOrder.php index 063c2e21..8e02117b 100644 --- a/webapp/models/OutAppOrder.php +++ b/webapp/models/OutAppOrder.php @@ -7,4 +7,34 @@ use phpcommon\SqlHelper; class OutAppOrder extends BaseModel { + const PENDING_STATE = 0; + const FINISHED_STATE = 1; + const FAILED_STATE = 2; + + public static function find($orderId) + { + $row = SqlHelper::ormSelectOne( + myself()->_get(), + 't_outapp_order', + array( + 'order_id' => $orderId, + ) + ); + return $row; + } + + public static function markFinished($orderId) + { + SqlHelper::update( + myself()->_get(), + 't_outapp_order', + array( + 'order_id' => $orderId, + ), + array( + 'state' => self::FINISHED_STATE, + ) + ); + } + } diff --git a/webapp/services/callback/InAppPurchase.php b/webapp/services/callback/InAppPurchase.php index a4a5cbf3..5362b1e9 100644 --- a/webapp/services/callback/InAppPurchase.php +++ b/webapp/services/callback/InAppPurchase.php @@ -20,42 +20,50 @@ class InAppPurchase { public function process() { - error_log("buyGoodsDirect --- " . json_encode($_REQUEST)); + error_log('ShopInappPurchaseDiamonds:' . json_encode($_REQUEST, JSON_PRETTY_PRINT)); + error_log('----- inappPurchaseDiamonds -----'); + $body = json_decode(file_get_contents('php://input'), true); + error_log('body:' . json_encode($body)); + $channel = $body['channel']; + $records = $body['records']; + $sign = $body['sign']; - // let repdata = { - // account_id: string - // order_id: string - // status: string - // id: string - // txhash: string - // } - // 我返回给你这些数据和一个sign字段, - // sign使用上面 repdata 按key 顺序排后, 组成key1=val1&key2=val2后, 使用hmac_sha256 hash, key是 - // PENDING = 0, // 初始状态 - // TRANSFERING = 1, //只有国库模式才会有该状态 - // TRANSFERED = 2, //只有国库模式才会有该状态 - // SUCCESS = 9, // 成功的最终状态 - // TRANSFER_FAIL = 98, // 转账错误 - // FAIL = 99, // 也是错误 - // + // { + // channel: 'google', + // sign: '123456677' // 签名字段 + // records: [{ + // productId: '2999', // 从google play console获取的product id + // gameOrderId: '1231321312', // 开始支付时, 从游戏相关服务那获得的订单id + // orderId: 'GPA.3355-1172-9416-16839', // 从google develope API 获取的订单id + // status: 9, // 订单状态, 上报的订单状态一般只有2种情况, 9: 支付成功, 96: 用户退款 + // }] + // } + // let reportData: any = { + // channel: 'google', + // records, + // } + // const hashSort = '' + // const signStr = 'channel=google&' + records.map(record =>Object.keys(record).sort().map(key => `${key}=${record[key]}`).join('&')).join('&') - $account_id = getReqVal('account_id', ''); - $order_id = getReqVal('order_id', ''); - $status = getReqVal('status', ''); - $id = getReqVal('id', ''); - $txhash = getReqVal('txhash', ''); + // const sign = hmacsha256(signStr, hashSort) - $sign = getReqVal('sign', ''); + // 定义一个空数组,用来存放每个记录的键值对字符串 + $record_strings = array(); - $data = array( - 'account_id' => $account_id, - 'id' => $id, - 'order_id' => $order_id, - 'status' => $status, - 'txhash' => $txhash, - ); + // 遍历 records 数组,对每个记录进行排序和拼接 + foreach ($records as $record) { + // 对记录的键进行升序排序 + ksort($record); + // 把记录的键值对用等号连接,然后用 & 连接成一个字符串 + $record_string = http_build_query($record); + // 把字符串加入到 record_strings 数组中 + $record_strings[] = $record_string; + } - $hash_data = http_build_query($data); + // 把 record_strings 数组用 & 连接成一个字符串 + $records_string = implode("&", $record_strings); + + $hash_data = 'channel=' . $channel . '&' . $records_string; $signature = hash_hmac('sha256', $hash_data, BUY_SERVER_PKEY); @@ -64,163 +72,136 @@ class InAppPurchase { return; } - error_log("buyGoodsDirect-------" . $order_id . "---" . $status); - $conn = myself()->_getMysql(''); + // 有三种情况: + // 1. 从商城购买钻石,有订单号 + // 2. 站外充值钻石,没有订单号 + // 3. appstore 退款,没有订单号 - $order = SqlHelper::selectOne($conn, 't_shop_buy_order', array('address', 'id', 'item_id', 'goods_num', 'status'), array('order_id' => $order_id)); - if (!$order) { - $this->_rspErr(2, "order not found: {$order_id}"); - return; - } + for ($i = 0; $i < count($records); $i++) { + $record = $records[$i]; - $id = $order['id']; - $goods_num = $order['goods_num']; - $o_status = $order['status']; - $address = $order['address']; + $product_id = $record['productId']; + $order_id = $record['gameOrderId']; + $out_order_id = $record['orderId']; + $status = $record['status']; - if ($o_status != 0) { - $this->_rspErr(1, "order status error, status: {$o_status}"); - return; - } + switch ($status) { + case 9: { + $status = 1; + if (empty($order_id)) { + if (empty($product_id)) { + $this->_rspErr(2, "product_id is empty"); + return; + } - $buyStatus = 0; // 1: 成功, 2: 失败 - switch ($status) { - case "9": - $buyStatus = 1; - // 充值成功,开始首充奖励 - $this->beginFirstTupop($address); - break; - case "99": - case "98": - $buyStatus = 2; - break; - default: - error_log("buyGoodsDirect--- " . $order_id . " --- " . $status); - $this->_rspErr(1, "status error, status: {$status}"); - break; - } + // $goods = mt\ShopGoods::getByProductId($product_id); - SqlHelper::update($conn, 't_shop_buy_order', array('order_id' => $order_id), array('status' => $buyStatus)); + return; + } - if ($buyStatus == 2) { - $this->_rspErr(2, "buyStatus error, buyStatus: {$buyStatus}"); - return; - } + $order = SqlHelper::selectOne($conn, 't_web2_order', array('address', 'id', 'item_id', 'goods_num', 'status'), array('order_id' => $order_id, 'status' => 0)); + error_log('process order ' . json_encode($order)); + if (!$order) { + $this->_rspErr(3, "order not found, order_id: {$order_id}"); + return; + } - // 以下是看商品表中是否配置了充值额外奖励 - $goods = ShopGoods::get($id); - error_log("buyGoodsDirect---" . json_encode($goods)); - $goods_num = $order['goods_num']; - $bundle_size = $goods['bonus_num'] ? $goods['bonus_num'] : 0; - $item_num = $goods_num * $bundle_size; - $item_id = $goods['bonus']; - $meta = Item::get($item_id); - error_log("buyGoodsDirect---" . $item_id . "---" . $item_num . "---" . $bundle_size . "---" . $meta['name']); - if ($meta && $item_num > 0) { - // $address = $order['address']; - $account_id = $this->getAccountId($address); + SqlHelper::update($conn, 't_web2_order', array('order_id' => $order_id), array('status' => $status, 'channel' => $channel, 'out_order_id' => $out_order_id)); - if ($item_id == V_ITEM_DIAMOND) { - $event = [ - 'name' => LogService::RECHARGE_CEBG_BONUS, - 'val' => $item_num - ]; - LogService::productDiamondCallback(['account_id' => $account_id], $event); + $id = $order['id']; + $goods = ShopGoods::get($id); + // 这里命名混乱了, 购买个数,一捆个数命名冲突 + $goods_num = $order['goods_num']; + $bundle_size = $goods['goods_num']; + $item_num = $goods_num * $bundle_size; + $item_id = $goods['goods_id']; + $address = $order['address']; + if (empty($address)) { + $this->_rspErr(4, "address is empty"); + return; + } + $account_id = $this->getAccountId($address); + if (empty($account_id)) { + $this->_rspErr(5, "account_id is empty"); + return; + } + + if ($item_id == V_ITEM_DIAMOND) { + $event = [ + 'name' => LogService::RECHARGE_DIAMOND, + 'val' => $item_num + ]; + LogService::productDiamond(['account_id' => $account_id], $event); + } + + $this->_addGoods($address, array( + 'goods_id' => $item_id, + 'goods_num' => $item_num, + 'id' => $id, + )); + } + break; + case 96: + $status = 3; + if (empty($order_id)) { + if (empty($product_id)) { + $this->_rspErr(2, "product_id is empty"); + return; + } + // $goods = mt\ShopGoods::getByProductId($product_id); + + return; + } + // 退款 + $order = SqlHelper::selectOne($conn, 't_web2_order', array('address', 'id', 'item_id', 'goods_num', 'status'), array('order_id' => $order_id, 'status' => 1)); + if (!$order) { + $this->_rspErr(3, "order not found, order_id: {$order_id}"); + return; + } + SqlHelper::update($conn, 't_web2_order', array('order_id' => $order_id), array('status' => $status)); + + $id = $order['id']; + $goods = ShopGoods::get($id); + // 这里命名混乱了, 购买个数,一捆个数命名冲突 + $goods_num = $order['goods_num']; + $bundle_size = $goods['goods_num']; + $item_num = $goods_num * $bundle_size; + $item_id = $goods['goods_id']; + $address = $order['address']; + if (empty($address)) { + $this->_rspErr(4, "address is empty"); + return; + } + $account_id = $this->getAccountId($address); + if (empty($account_id)) { + $this->_rspErr(5, "account_id is empty"); + return; + } + + if ($item_id == V_ITEM_DIAMOND) { + $event = [ + 'name' => LogService::RECHARGE_DIAMOND, + 'val' => -$item_num + ]; + LogService::productDiamond(['account_id' => $account_id], $event); + } + + $this->_decGoods($address, array( + 'goods_id' => $item_id, + 'goods_num' => $item_num, + 'id' => $id, + )); + break; + default: + $status = 0; + $this->_rspErr(1, "status is not 9 or 96"); + return; + break; } - - error_log("buyGoodsDirect---" . $address . "---" . $item_id . "---" . $item_num); - - $this->_addGoods($address, array( - 'goods_id' => $item_id, - 'goods_num' => $item_num, - 'id' => $id, - )); } $this->_rspOk(); } - private function getAccountId($address) - { - $row = SqlHelper::ormSelectOne( - myself()->_getMysql($address), - 't_user', - array( - 'address' => $address - ) - ); - return $row['account_id']; - } - - private function _addGoods($address, $goods) - { - $itemService = new ShopAddItemService(); - $item_id = $goods['goods_id']; - $goods_num = $goods['goods_num']; - - $id = null; - if ($goods['id']) { - $id = $goods['id']; - } - - error_log('_addGoods ' . $address . ' item_id ' . $item_id . ' goods_num ' . $goods_num . ' id ' . $id); - $itemService->addItem($address, $item_id, $goods_num); - if ($id) { - ShopBuyRecord::addWithAddress($address, $id, $goods_num); - } - } - - private function beginFirstTupop($address) - { - $conn = myself()->_getMysql(''); - - $exist = SqlHelper::selectOne( - $conn, - 't_first_topup', - array('address'), - array('address' => $address) - ); - if ($exist) { - return; - } - - // 开始首充奖励活动进程 - $chk = SqlHelper::insert( - $conn, - 't_first_topup', - array( - 'address' => $address, - 'createtime' => myself()->_getNowTime(), - 'status1' => 0, - 'status2' => 0, - 'status3' => 0, - ) - ); - if (!$chk) { - return; - } - } - - private function _rspOk() - { - echo json_encode(array( - 'errcode' => 0, - 'errmsg' => "callback success", - )); - } - - private function _rspErr($errcode, $errmsg) - { - if (SERVER_ENV != _ONLINE) { - error_log(json_encode(array( - 'errcode' => $errcode, - 'errmsg' => $errmsg, - ))); - } - echo json_encode(array( - 'errcode' => $errcode, - 'errmsg' => $errmsg, - )); - } } diff --git a/webapp/services/callback/OutAppPurchase.php b/webapp/services/callback/OutAppPurchase.php index 63e5b045..4b7366fc 100644 --- a/webapp/services/callback/OutAppPurchase.php +++ b/webapp/services/callback/OutAppPurchase.php @@ -4,7 +4,10 @@ namespace services; require_once("mt/ShopGoods.php"); require_once("mt/Item.php"); + require_once("models/ShopBuyRecord.php"); +require_once("models/OutAppOrder.php"); + require_once("services/LogService.php"); require_once("ShopAddItemService.php"); @@ -12,7 +15,9 @@ use phpcommon\SqlHelper; use mt\ShopGoods; use mt\Item; + use models\ShopBuyRecord; +use models\OutAppOrder; use services\LogService; @@ -29,61 +34,54 @@ class OutAppPurchase { // id: string // txhash: string // } - // 我返回给你这些数据和一个sign字段, - // sign使用上面 repdata 按key 顺序排后, 组成key1=val1&key2=val2后, 使用hmac_sha256 hash, key是 + // 我返回给你这些数据和一个sign字段, + // sign使用上面 repdata 按key 顺序排后, 组成key1=val1&key2=val2后, 使用hmac_sha256 hash, key是 // PENDING = 0, // 初始状态 // TRANSFERING = 1, //只有国库模式才会有该状态 // TRANSFERED = 2, //只有国库模式才会有该状态 // SUCCESS = 9, // 成功的最终状态 // TRANSFER_FAIL = 98, // 转账错误 // FAIL = 99, // 也是错误 - // - - $account_id = getReqVal('account_id', ''); - $order_id = getReqVal('order_id', ''); + // + if (!$this->verifySign()) { + error_log("verifySign error --- " . json_encode($_REQUEST)); + myself()->_rspErr(1, 'verifySign error'); + return; + } + $accountId = getReqVal('account_id', ''); $status = getReqVal('status', ''); $id = getReqVal('id', ''); $txhash = getReqVal('txhash', ''); - $sign = getReqVal('sign', ''); - $data = array( - 'account_id' => $account_id, - 'id' => $id, - 'order_id' => $order_id, - 'status' => $status, - 'txhash' => $txhash, - ); - - $hash_data = http_build_query($data); - - $signature = hash_hmac('sha256', $hash_data, BUY_SERVER_PKEY); - - if ($signature != $sign) { - $this->_rspErr(1, "signature error, signature: {$signature}, sign: {$sign}"); + $orderId = getReqVal('order_id', ''); + $orderDb = OutAppOrder::find($orderId); + if (!$orderDb) { + myself()->_rspErr(2, 'not found order'); + return; + } + if ($orderDb['status'] != OutAppOrder::FINISHED_STATE) { + myself()->_rspErr(0, 'order is finished'); + return; + } + OutAppOrder::markFinished($orderDb['order_id']); + $orderDb = OutAppOrder::find($orderId); + if (!$orderDb) { + myself()->_rspErr(2, 'not found order'); + return; + } + if ($orderDb['status'] == OutAppOrder::FINISHED_STATE) { + myself()->_rspErr(0, 'order is finished'); return; } - - error_log("buyGoodsDirect-------" . $order_id . "---" . $status); $conn = myself()->_getMysql(''); - $order = SqlHelper::selectOne($conn, 't_shop_buy_order', array('address', 'id', 'item_id', 'goods_num', 'status'), array('order_id' => $order_id)); - if (!$order) { - $this->_rspErr(2, "order not found: {$order_id}"); - return; - } - $id = $order['id']; $goods_num = $order['goods_num']; $o_status = $order['status']; $address = $order['address']; - if ($o_status != 0) { - $this->_rspErr(1, "order status error, status: {$o_status}"); - return; - } - $buyStatus = 0; // 1: 成功, 2: 失败 switch ($status) { case "9": @@ -190,4 +188,10 @@ class OutAppPurchase { } } + + private function verifySign() + { + + } + }