payRequest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <?php
  2. /**
  3. * 统一的预创建订单请求对象
  4. * @version
  5. * 1.0.0 Created at 2017-12-21. by --gwang
  6. * @author gwang (mail@wanggangzero.cn)
  7. * @copyright ? 2017-12-21, SJZ LoyalSoft Corporation & gwang. All rights reserved.
  8. */
  9. class PayRequest extends loyalsoft\Object_ext {
  10. const orderTab = 'tab_order_tab';
  11. /**
  12. * @var bool 是否测试(金额降低100倍)(线上版千万别瞎改)
  13. */
  14. const isTest = false;
  15. /**
  16. * @var string 充值回调透传参数(len=250)
  17. */
  18. public $callbackInfo;
  19. /**
  20. * 各平台取自己的配置信息
  21. * @var string 充值回调地址(len=100)
  22. */
  23. public $notifyUrl;
  24. /**
  25. * @var string 订单ID (len=30)
  26. */
  27. public $cpOrderId;
  28. /**
  29. * @var float 充值金额 (单位:元) (特:入库时会x100转为分)
  30. */
  31. public $amount;
  32. /**
  33. * @var string 货币类型(默认:CNY)
  34. */
  35. public $currency = 'CNY';
  36. /**
  37. * @var string 平台UID
  38. */
  39. public $accountId;
  40. /**
  41. * @var int 游戏中的分区id
  42. */
  43. public $zoneid;
  44. /**
  45. * @var string 渠道
  46. */
  47. public $channel;
  48. /**
  49. * @var string 道具id
  50. */
  51. public $product_id;
  52. /**
  53. * @var string 道具名称
  54. */
  55. public $product_name;
  56. /**
  57. *
  58. * @var string 道具描述
  59. */
  60. public $product_desc;
  61. /**
  62. * @var string 道具单价
  63. */
  64. public $product_price;
  65. /**
  66. * @var int (购买)道具数量
  67. */
  68. public $product_count;
  69. /**
  70. * @var 防沉迷年龄段 ( -1 未实名, 0(0~7) 8(8~16) 16(16~18) 18(18+成年)
  71. * #[optional]
  72. */
  73. public $ageRange = -1;
  74. /**
  75. * @var string 公会id
  76. */
  77. public $party_id;
  78. public $party_name;
  79. /**
  80. * @var string 签名算法, 目前固定为MD5
  81. */
  82. public $signType = "MD5";
  83. /**
  84. *
  85. * @var string MD5(签名内容+apiKey)
  86. */
  87. public $sign;
  88. /**
  89. * @var string 客户端版本号
  90. */
  91. public $clientVer;
  92. /**
  93. * @var int 订单编号
  94. */
  95. private $order_id;
  96. /**
  97. *
  98. * @return array 订单写入数据库
  99. */
  100. public function InserDataBase() {
  101. // return false; # 2019.10.9 关闭充值
  102. if (self::isTest) {
  103. $this->channel = 'tester'; # 测试
  104. }
  105. $arr = get_object_vars($this); # 转为数组
  106. $arr["uid"] = $arr["accountId"]; # 转换下变量名称
  107. $arr["amount"] = $arr["amount"] * 100; # 拉起平台时传的单位是元,比如UC还有易接,而游戏后台存储的是分
  108. unset($arr["signType"]); # 毙掉后台不需要的数据
  109. unset($arr["sign"]);
  110. unset($arr["accountId"]);
  111. unset($arr['order_id']);
  112. unset($arr['ageRange']);
  113. unset($arr['product_desc']);
  114. unset($arr['clientVer']);
  115. $n = loyalsoft\daoInst()->update(self::orderTab)
  116. ->data($arr)
  117. ->where('id')->eq($this->order_id)
  118. ->exec();
  119. return $n == 1;
  120. }
  121. /**
  122. * @return array 拉取签名参数数组
  123. */
  124. public function uc_GetData2Sign() {
  125. $sid = array('callbackInfo', 'amount', 'notifyUrl', 'cpOrderId', 'accountId');
  126. $arr = array();
  127. foreach (get_object_vars($this) as $key => $value) {
  128. if (in_array($key, $sid)) {
  129. $arr[$key] = $value;
  130. }
  131. }
  132. return $arr;
  133. }
  134. /**
  135. * @return array 普通返回值数组
  136. */
  137. public function retData() {
  138. $sid = array('callbackInfo', 'amount', 'product_price', 'product_count', 'notifyUrl', 'cpOrderId');
  139. $arr = array();
  140. foreach (get_object_vars($this) as $key => $value) {
  141. if (in_array($key, $sid)) {
  142. $arr[$key] = $value;
  143. }
  144. }
  145. return $arr;
  146. }
  147. /**
  148. * @return array 小七手游订单结构(未签名)
  149. * @deprecated since version 0 已废弃
  150. */
  151. public function x7sy_retData() {
  152. $ret = array(
  153. 'extends_info_data' => $this->callbackInfo, // // 透传参数
  154. 'game_area' => $this->zoneid,
  155. 'game_guid' => $this->accountId,
  156. 'game_level' => 0,
  157. 'game_orderid' => $this->cpOrderId,
  158. 'game_price' => $this->product_price, // // 对方要求单位:元
  159. 'game_role_id' => "no",
  160. 'game_role_name' => "no",
  161. 'notify_id' => "3788", // // 如果只有一个可以设置为-1,但不可以设置为0
  162. 'subject' => $this->product_name
  163. );
  164. return $ret;
  165. }
  166. /**
  167. * @return array 易接返回值数组
  168. * @deprecated since version 0 已废弃
  169. */
  170. public function yijie_retData() {
  171. return $this->retData();
  172. }
  173. /**
  174. * 华为 订单通知魔改版
  175. * @param type $params 参考:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-References/server-data-model-0000001050986133#section264617465219
  176. */
  177. static function From_HW($params) {
  178. $infoArr = array();
  179. \loyalsoft\CLog::pay($params);
  180. $callbackInfo = $params['developerPayload'];
  181. $infos = explode(',', $callbackInfo); # uid,zoneid,cpOrderId,productId
  182. $infoArr['orderId'] = $params['orderId'];
  183. $infoArr['accountId'] = $infos[0]; # uid
  184. $infoArr['amount'] = number_format($params['price'] / 100, 2, '.', ''); # 分转元 # 华为给过来是分, 这里先转换为元, 入库时再统一以分为单位
  185. $infoArr['currency'] = $params['currency']; # 如果是海外会有不同的货币类型
  186. $infoArr['product_id'] = $params['productId'];
  187. $infoArr['product_count'] = 1;
  188. $infoArr['channel'] = "huawei"; # 华为
  189. $infoArr['zoneid'] = (int) $infos[1]; # 分区信息
  190. $infoArr['callbackInfo'] = $callbackInfo;
  191. $o = new payRequest($infoArr);
  192. $o->cpOrderId = $infos[2]; # 重新修订下cpOrderId
  193. return $o;
  194. }
  195. public function __construct($arg = null) {
  196. parent::__construct($arg);
  197. if (!$this->accountId || !$this->product_id || !$this->amount || !$this->channel) {
  198. \loyalsoft\CLog::pay('参数缺失');
  199. exit('{"err":1,"msg":"参数缺失!"}');
  200. }
  201. $this->callbackInfo = $this->accountId . "," . $this->zoneid;
  202. if (self::isTest) {
  203. $this->amount *= 0.01; // 也可以改成固定值 0.01, 更省钱些.
  204. }
  205. if (!self::isTest and $this->amount) {
  206. new loyalsoft\Req(null); # 初始化
  207. \loyalsoft\req()->CV = $this->clientVer; # 设定配置数据版本号(2024.12增补)
  208. // var_dump(loyalsoft\req()->CV);
  209. $itemMo = loyalsoft\GameConfig::shop_getItem($this->product_id);
  210. if (null != $itemMo && $this->amount != $itemMo->price) {
  211. \loyalsoft\CLog::pay($this->product_id);
  212. // \loyalsoft\CLog::pay(var_export(loyalsoft\gMem()));
  213. \loyalsoft\CLog::pay(loyalsoft\GameConfig::shop_getItem($this->product_id)->price);
  214. \loyalsoft\CLog::pay('金额不符: ' . $this->amount . " <=> " . $itemMo->price);
  215. exit('{"err":1,"msg":"金额不符!"}');
  216. }
  217. }
  218. loyalsoft\daoInst()->insert(self::orderTab) # 创建一个新的订单
  219. ->data(array('order_ts' => loyalsoft\now()))
  220. ->exec();
  221. $this->order_id = loyalsoft\daoInst()->select('last_insert_id() as id')->fetch('id'); # 取刚才创建的订单的自增id
  222. $this->cpOrderId = LoyalsoftAPPID . \date('ymdHi') . substr(sprintf("%1$06d", $this->order_id), -6); # 商户唯一id(尾部附加自增id后6位数)
  223. $this->callbackInfo .= "," . $this->cpOrderId; # 将cpOrderId追加到透传参数中
  224. $this->notifyUrl = $this::get_notify_url(); # 自动提取支付回调地址
  225. }
  226. /**
  227. * PS. 仅限pay.php作为入口使用才行
  228. * @return string 获取支付回调地址,
  229. */
  230. public function get_notify_url() {
  231. $url = "https://" . $_SERVER['HTTP_HOST'] . $_SERVER["REQUEST_URI"];
  232. switch ($this->getPlatStr()) {
  233. case "hykb":
  234. $nurl = substr($url, 0, strpos($url, "pay.php")) . "notify_hykb.php";
  235. break;
  236. default:
  237. $nurl = substr($url, 0, strpos($url, "pay.php")) . "notify.php";
  238. break;
  239. }
  240. return $nurl; # 回调地址
  241. }
  242. /**
  243. * 未成年检查 (国内版)
  244. * @return type
  245. */
  246. public function AntiAdditionCheck() {
  247. $uid = $this->accountId;
  248. $ageRange = $this->ageRange;
  249. $ageRange = 18;
  250. $amt = $this->amount;
  251. if (isset($uid) && isset($ageRange)) {
  252. $monthTS = loyalsoft\TimeUtil::tsMonthBegin(); # 本月初起始时间戳
  253. $dayTS = loyalsoft\TimeUtil::tsDayBegin(); # 当天起始时间戳
  254. $monthTotal = loyalsoft\daoInst()
  255. ->select("sum(`amount`)/100 as total") # 总金额(单位:分转元)
  256. ->from(self::orderTab)
  257. ->where("uid")->eq($uid) # 该玩家
  258. ->andWhere("status")->eq(1) # 成交订单
  259. ->andWhere("order_ts")->ge($monthTS) # 本月
  260. ->fetch('total');
  261. if (!is_numeric($monthTotal)) {
  262. $monthTotal = 0;
  263. }
  264. $dayTotal = loyalsoft\daoInst()
  265. ->select("sum(`amount`)/100 as total") # 总金额(单位:分转元)
  266. ->from(self::orderTab)
  267. ->where("uid")->eq($uid) # 该玩家
  268. ->andWhere("status")->eq(1) # 成交订单
  269. ->andWhere("order_ts")->ge($dayTS) # 今日
  270. ->fetch('total');
  271. if (!is_numeric($dayTotal)) {
  272. $dayTotal = 0;
  273. }
  274. if ($ageRange < 0 || !is_numeric($ageRange)) {
  275. return \payResp::err(1, "未完成实名认证的用户暂不提供充值.");
  276. } else if ($ageRange < 8) {
  277. return \payResp::err(1, "未满8周岁的用户暂不提供充值.");
  278. } else if ($ageRange < 16) { # 单笔/每月: 50,200
  279. if ($amt > 50) {
  280. return \payResp::err(1, "单笔充值金额不高于50¥");
  281. }
  282. // if (($amt + $dayTotal) > 100) {
  283. // return \Resp::err(1, "单日累计充值金额不高于100¥");
  284. // }
  285. if (($amt + $monthTotal) > 200) {
  286. return \payResp::err(1, "当月累计充值金额不高于200¥");
  287. }
  288. } else if ($ageRange < 18) { # 单笔/每月: 100,400
  289. if ($amt > 100) {
  290. return \payResp::err(1, "单笔充值金额不高于100¥");
  291. }
  292. // if (($amt + $dayTotal) > 100) {
  293. // return \Resp::err(1, "单日累计充值金额不高于100¥");
  294. // }
  295. if (($amt + $monthTotal) > 400) {
  296. return \payResp::err(1, "当月累计充值金额不高于400¥");
  297. }
  298. }
  299. return \payResp::ok("ok");
  300. }
  301. return \payResp::err(-1, "参数不足"); # 其他情况
  302. }
  303. /**
  304. * 获得用户的平台字符串
  305. * @return string
  306. */
  307. function getPlatStr() {
  308. if (strrpos($this->accountId, '_') > 0) {
  309. return substr($this->accountId, strrpos($this->accountId, '_') + 1); # 提取平台字符串
  310. } else {
  311. return "";
  312. }
  313. //PS. substr() 函数返回字符串的一部分 strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。
  314. }
  315. /**
  316. * 获得用户的平台唯一id.
  317. * @return string
  318. */
  319. public function getPlatOid() {
  320. return substr($this->accountId, 0, strrpos($this->accountId, '_')); # 提取平台字符串
  321. //PS. substr() 函数返回字符串的一部分 strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。
  322. }
  323. }