PaymentController.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Components\AlipaySubmit;
  4. use App\Components\Helpers;
  5. use App\Components\Yzy;
  6. use App\Http\Models\Coupon;
  7. use App\Http\Models\Goods;
  8. use App\Http\Models\Order;
  9. use App\Http\Models\Payment;
  10. use App\Http\Models\PaymentCallback;
  11. use Auth;
  12. use DB;
  13. use Exception;
  14. use Illuminate\Http\Request;
  15. use Log;
  16. use Payment\Client\Charge;
  17. use Response;
  18. use Validator;
  19. /**
  20. * 支付控制器
  21. *
  22. * Class PaymentController
  23. *
  24. * @package App\Http\Controllers
  25. */
  26. class PaymentController extends Controller
  27. {
  28. protected static $systemConfig;
  29. function __construct()
  30. {
  31. self::$systemConfig = Helpers::systemConfig();
  32. }
  33. // 创建支付单
  34. public function create(Request $request)
  35. {
  36. $goods_id = $request->input('goods_id');
  37. $coupon_sn = $request->input('coupon_sn');
  38. $goods = Goods::query()->where('status', 1)->where('id', $goods_id)->first();
  39. if (!$goods) {
  40. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:商品或服务已下架']);
  41. }
  42. // 判断是否开启有赞云支付
  43. if (!self::$systemConfig['is_youzan'] && !self::$systemConfig['is_alipay'] && !self::$systemConfig['is_f2fpay']) {
  44. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:系统并未开启在线支付功能']);
  45. }
  46. // 判断是否存在同个商品的未支付订单
  47. $existsOrder = Order::uid()->where('status', 0)->where('goods_id', $goods_id)->exists();
  48. if ($existsOrder) {
  49. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:尚有未支付的订单,请先去支付']);
  50. }
  51. // 限购控制
  52. $strategy = self::$systemConfig['goods_purchase_limit_strategy'];
  53. if ($strategy == 'all' || ($strategy == 'package' && $goods->type == 2) || ($strategy == 'free' && $goods->price == 0) || ($strategy == 'package&free' && ($goods->type == 2 || $goods->price == 0))) {
  54. $noneExpireOrderExist = Order::uid()->where('status', '>=', 0)->where('is_expire', 0)->where('goods_id', $goods_id)->exists();
  55. if ($noneExpireOrderExist) {
  56. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:商品不可重复购买']);
  57. }
  58. }
  59. // 单个商品限购
  60. if ($goods->is_limit == 1) {
  61. $noneExpireOrderExist = Order::uid()->where('status', '>=', 0)->where('goods_id', $goods_id)->exists();
  62. if ($noneExpireOrderExist) {
  63. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:此商品每人限购1次']);
  64. }
  65. }
  66. // 使用优惠券
  67. if ($coupon_sn) {
  68. $coupon = Coupon::query()->where('status', 0)->whereIn('type', [1, 2])->where('sn', $coupon_sn)->first();
  69. if (!$coupon) {
  70. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:优惠券不存在']);
  71. }
  72. // 计算实际应支付总价
  73. $amount = $coupon->type == 2 ? $goods->price * $coupon->discount / 10 : $goods->price - $coupon->amount;
  74. $amount = $amount > 0 ? round($amount, 2) : 0; // 四舍五入保留2位小数,避免无法正常创建订单
  75. } else {
  76. $amount = $goods->price;
  77. }
  78. // 价格异常判断
  79. if ($amount < 0) {
  80. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:订单总价异常']);
  81. } elseif ($amount == 0) {
  82. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:订单总价为0,无需使用在线支付']);
  83. }
  84. // 验证账号是否存在有效期更长的套餐
  85. if ($goods->type == 2) {
  86. $existOrderList = Order::uid()->with(['goods'])->whereHas('goods', function ($q) {
  87. $q->where('type', 2);
  88. })->where('is_expire', 0)->where('status', 2)->get();
  89. foreach ($existOrderList as $vo) {
  90. if ($vo->goods->days > $goods->days) {
  91. return Response::json(['status' => 'fail', 'data' => '', 'message' => '支付失败:您已存在有效期更长的套餐,只能购买流量包']);
  92. }
  93. }
  94. }
  95. DB::beginTransaction();
  96. try {
  97. $orderSn = date('ymdHis') . mt_rand(100000, 999999);
  98. $sn = makeRandStr(12);
  99. // 支付方式
  100. if (self::$systemConfig['is_youzan']) {
  101. $pay_way = 2;
  102. } elseif (self::$systemConfig['is_alipay']) {
  103. $pay_way = 4;
  104. } elseif (self::$systemConfig['is_f2fpay']) {
  105. $pay_way = 5;
  106. }
  107. // 生成订单
  108. $order = new Order();
  109. $order->order_sn = $orderSn;
  110. $order->user_id = Auth::user()->id;
  111. $order->goods_id = $goods_id;
  112. $order->coupon_id = !empty($coupon) ? $coupon->id : 0;
  113. $order->origin_amount = $goods->price;
  114. $order->amount = $amount;
  115. $order->expire_at = date("Y-m-d H:i:s", strtotime("+" . $goods->days . " days"));
  116. $order->is_expire = 0;
  117. $order->pay_way = $pay_way;
  118. $order->status = 0;
  119. $order->save();
  120. // 生成支付单
  121. if (self::$systemConfig['is_youzan']) {
  122. $yzy = new Yzy();
  123. $result = $yzy->createQrCode($goods->name, $amount * 100, $orderSn);
  124. if (isset($result['error_response'])) {
  125. Log::error('【有赞云】创建二维码失败:' . $result['error_response']['msg']);
  126. throw new Exception($result['error_response']['msg']);
  127. }
  128. } elseif (self::$systemConfig['is_alipay']) {
  129. $parameter = ["service" => "create_forex_trade", // WAP:create_forex_trade_wap ,即时到帐:create_forex_trade
  130. "partner" => self::$systemConfig['alipay_partner'], "notify_url" => self::$systemConfig['website_url'] . "/api/alipay", // 异步回调接口
  131. "return_url" => self::$systemConfig['website_url'], "out_trade_no" => $orderSn, // 订单号
  132. "subject" => "Package", // 订单名称
  133. //"total_fee" => $amount, // 金额
  134. "rmb_fee" => $amount, // 使用RMB标价,不再使用总金额
  135. "body" => "", // 商品描述,可为空
  136. "currency" => self::$systemConfig['alipay_currency'], // 结算币种
  137. "product_code" => "NEW_OVERSEAS_SELLER", "_input_charset" => "utf-8"];
  138. // 建立请求
  139. $alipaySubmit = new AlipaySubmit(self::$systemConfig['alipay_sign_type'], self::$systemConfig['alipay_partner'], self::$systemConfig['alipay_key'], self::$systemConfig['alipay_private_key']);
  140. $result = $alipaySubmit->buildRequestForm($parameter, "post", "确认");
  141. } elseif (self::$systemConfig['is_f2fpay']) {
  142. // TODO:goods表里增加一个字段用于自定义商品付款时展示的商品名称,
  143. // TODO:这里增加一个随机商品列表,根据goods的价格随机取值
  144. $result = Charge::run("ali_qr", ['use_sandbox' => false, "partner" => self::$systemConfig['f2fpay_app_id'], 'app_id' => self::$systemConfig['f2fpay_app_id'], 'sign_type' => 'RSA2', 'ali_public_key' => self::$systemConfig['f2fpay_public_key'], 'rsa_private_key' => self::$systemConfig['f2fpay_private_key'], 'notify_url' => self::$systemConfig['website_url'] . "/api/f2fpay", // 异步回调接口
  145. 'return_url' => self::$systemConfig['website_url'], 'return_raw' => false], ['body' => '', 'subject' => self::$systemConfig['f2fpay_subject_name'], 'order_no' => $orderSn, 'amount' => $amount,]);
  146. }
  147. $payment = new Payment();
  148. $payment->sn = $sn;
  149. $payment->user_id = Auth::user()->id;
  150. $payment->oid = $order->oid;
  151. $payment->order_sn = $orderSn;
  152. $payment->pay_way = 1;
  153. $payment->amount = $amount;
  154. if (self::$systemConfig['is_youzan']) {
  155. $payment->qr_id = $result['response']['qr_id'];
  156. $payment->qr_url = $result['response']['qr_url'];
  157. $payment->qr_code = $result['response']['qr_code'];
  158. $payment->qr_local_url = $this->base64ImageSaver($result['response']['qr_code']);
  159. } elseif (self::$systemConfig['is_alipay']) {
  160. $payment->qr_code = $result;
  161. } elseif (self::$systemConfig['is_f2fpay']) {
  162. $payment->qr_code = $result;
  163. $payment->qr_url = 'http://qr.topscan.com/api.php?text=' . $result . '&bg=ffffff&fg=000000&pt=1c73bd&m=10&w=400&el=1&inpt=1eabfc&logo=https://t.alipayobjects.com/tfscom/T1Z5XfXdxmXXXXXXXX.png';
  164. $payment->qr_local_url = $payment->qr_url;
  165. }
  166. $payment->status = 0;
  167. $payment->save();
  168. // 优惠券置为已使用
  169. if (!empty($coupon)) {
  170. if ($coupon->usage == 1) {
  171. $coupon->status = 1;
  172. $coupon->save();
  173. }
  174. Helpers::addCouponLog($coupon->id, $goods_id, $order->oid, '在线支付使用');
  175. }
  176. DB::commit();
  177. if (self::$systemConfig['is_alipay']) { // Alipay返回支付信息
  178. return Response::json(['status' => 'success', 'data' => $result, 'message' => '创建订单成功,正在转到付款页面,请稍后']);
  179. } else {
  180. return Response::json(['status' => 'success', 'data' => $sn, 'message' => '创建订单成功,正在转到付款页面,请稍后']);
  181. }
  182. } catch (Exception $e) {
  183. DB::rollBack();
  184. Log::error('创建支付订单失败:' . $e->getMessage());
  185. return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建订单失败:' . $e->getMessage()]);
  186. }
  187. }
  188. // 支付单详情
  189. public function detail(Request $request, $sn)
  190. {
  191. $view['payment'] = Payment::uid()->with(['order', 'order.goods'])->where('sn', $sn)->firstOrFail();
  192. return Response::view('payment.detail', $view);
  193. }
  194. // 获取订单支付状态
  195. public function getStatus(Request $request)
  196. {
  197. $validator = Validator::make($request->all(), ['sn' => 'required|exists:payment,sn'], ['sn.required' => '请求失败:缺少sn', 'sn.exists' => '支付失败:支付单不存在']);
  198. if ($validator->fails()) {
  199. return Response::json(['status' => 'error', 'data' => '', 'message' => $validator->getMessageBag()->first()]);
  200. }
  201. $payment = Payment::uid()->where('sn', $request->sn)->first();
  202. if ($payment->status > 0) {
  203. return Response::json(['status' => 'success', 'data' => '', 'message' => '支付成功']);
  204. } elseif ($payment->status < 0) {
  205. return Response::json(['status' => 'error', 'data' => '', 'message' => '订单超时未支付,已自动关闭']);
  206. } else {
  207. return Response::json(['status' => 'fail', 'data' => '', 'message' => '等待支付']);
  208. }
  209. }
  210. // 回调日志
  211. public function callbackList(Request $request)
  212. {
  213. $status = $request->input('status', 0);
  214. $query = PaymentCallback::query();
  215. if (isset($status)) {
  216. $query->where('status', $status);
  217. }
  218. $view['list'] = $query->orderBy('id', 'desc')->paginate(10)->appends($request->except('page'));
  219. return Response::view('payment.callbackList', $view);
  220. }
  221. }