config['appid']) && !empty($this->config['private_key']); if ($this->isDemo() || !$hasCred) { return [ 'mode' => 'demo', 'channel' => 'alipay', 'simulate' => $simulate, 'config_missing' => !$hasCred, ]; } $biz = [ 'out_trade_no' => $order['order_no'], 'product_code' => 'FAST_INSTANT_TRADE_PAY', 'total_amount' => $this->money($order['amount']), 'subject' => $order['product_name'] ?: '商品购买', ]; $params = [ 'app_id' => $this->config['appid'], 'method' => 'alipay.trade.page.pay', 'format' => 'JSON', 'charset' => 'utf-8', 'sign_type' => 'RSA2', 'timestamp' => date('Y-m-d H:i:s'), 'version' => '1.0', 'notify_url' => site_url('pay/notify/alipay'), 'return_url' => site_url('order/success/' . $order['order_no']), 'biz_content' => json_encode($biz, JSON_UNESCAPED_UNICODE), ]; $params['sign'] = $this->sign($params); $gateway = $this->config['gateway'] ?: 'https://openapi.alipay.com/gateway.do'; return ['mode' => 'redirect', 'channel' => 'alipay', 'url' => $gateway . '?' . http_build_query($params)]; } /** RSA2 签名 */ private function sign(array $params): string { ksort($params); $str = ''; foreach ($params as $k => $v) { if ($v === '' || $v === null) continue; $str .= $k . '=' . $v . '&'; } $str = rtrim($str, '&'); $key = $this->normalizeKey($this->config['private_key'] ?? '', false); openssl_sign($str, $sign, $key, OPENSSL_ALGO_SHA256); return base64_encode($sign); } private function normalizeKey(string $key, bool $isPublic): string { $key = trim($key); if (strpos($key, '-----BEGIN') === 0) return $key; $head = $isPublic ? "-----BEGIN PUBLIC KEY-----\n" : "-----BEGIN RSA PRIVATE KEY-----\n"; $foot = $isPublic ? "\n-----END PUBLIC KEY-----" : "\n-----END RSA PRIVATE KEY-----"; return $head . chunk_split($key, 64, "\n") . $foot; } public function verifyNotify(array $data): ?string { if (empty($data['out_trade_no'])) return null; $status = $data['trade_status'] ?? ''; if ($status === 'TRADE_SUCCESS' || $status === 'TRADE_FINISHED') { // 生产环境应使用支付宝公钥对签名做严格验签后再返回 return $data['out_trade_no']; } return null; } }