['Authorization: Bearer ' . getenv('WPMP_API_KEY'), 'Content-Type: application/json'], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); if ($data !== null) curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data, JSON_THROW_ON_ERROR)); $body = curl_exec($curl); $status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); if ($body === false || $status < 200 || $status >= 300) throw new RuntimeException("WPMP HTTP $status"); return json_decode($body, true, 512, JSON_THROW_ON_ERROR); } function createOrder(string $orderNo, int $amountMinor, string $channel = 'WECHAT'): array { return wpmpRequest('', compact('orderNo', 'amountMinor', 'channel')); } function queryOrder(string $orderNo): array { return wpmpRequest('/' . rawurlencode($orderNo)); } // 在 POST 回调路由调用。$server 传入 $_SERVER,$raw 从 php://input 读取。 function receive(array $server, string $raw): array { $ts = $server['HTTP_X_WPMP_TIMESTAMP'] ?? ''; $id = $server['HTTP_X_WPMP_DELIVERY_ID'] ?? ''; $sig = $server['HTTP_X_WPMP_SIGNATURE'] ?? ''; $secret = getenv('WPMP_WEBHOOK_SECRET'); if (!$secret || !$id || !preg_match('/^[0-9]{10}$/D', $ts) || abs(time() - (int)$ts) > 300) throw new InvalidArgumentException('Invalid headers'); $expected = 'v1=' . hash_hmac('sha256', $ts . "\n" . $id . "\n" . $raw, $secret); if (!hash_equals($expected, $sig)) throw new InvalidArgumentException('Invalid signature'); $event = json_decode($raw, true, 512, JSON_THROW_ON_ERROR); if (empty($event['event_type']) || $event['event_type'] !== ($server['HTTP_X_WPMP_EVENT'] ?? '')) throw new InvalidArgumentException('Invalid event'); return [$id, $event]; }