返回

解决 PhonePe 自动扣款 API 报错 "Incorrect Request"

php

PhonePe 自动扣款 API (/v3/recurring/debit/init) 报 "Incorrect Request" 错误的排查与解决

最近在对接 PhonePe 的自动扣款 (Auto-Debit) API,调用 /v3/recurring/debit/init 接口时,总是返回 "status": "error","message": "Please check the inputs you have provided. [message = Incorrect Request.]" 错误。 遇到这种情况,确实让人头疼。下面我就把排查和解决这个问题的思路和方法分享给大家。

一、问题原因分析

报 "Incorrect Request" 这种错,通常意味着发给 PhonePe 服务器的请求数据有问题。具体来说,可能有以下几种原因:

  1. 请求参数缺失或格式错误: v3/recurring/debit/init 接口需要一系列参数,比如金额、用户信息、回调 URL 等。如果漏掉了某个必填参数,或者参数的格式不对(例如,数字写成了字符串),就会报错。

  2. 签名 (checksum) 错误: PhonePe 为了保证 API 调用的安全,采用了签名机制。如果计算签名的方法不对,或者使用了错误的密钥,服务器校验签名不通过,就会拒绝请求。

  3. 请求头 (Headers) 设置不正确: 有些 API 对请求头有特殊要求。 比如,Content-Type 可能需要设置为 application/json, 或者需要特定的 X-VERIFY 头信息。

  4. IP 地址未加入白名单: 有可能是你的服务器IP地址没有被添加到PhonePe允许的白名单内,从而导致访问受限。

  5. 编码问题 极少数情况也可能是字符编码引起的,比如不是UTF-8编码。

二、解决方案

针对上面分析的原因,可以按下面步骤逐一排查、解决问题。

1. 仔细核对请求参数

原理: 确保所有必填参数都已提供,并且参数值符合 PhonePe API 文档的要求。

操作步骤:

  • 认真阅读 PhonePe 关于 /v3/recurring/debit/init 接口的文档,找出所有必填参数。
  • 对照文档,逐个检查你的请求数据:
    • 有没有漏掉参数?
    • 参数名是否正确?(注意大小写)
    • 参数值的类型是否正确?(例如,金额应该是数字,不能是字符串)
    • 参数值的格式是否符合要求?(例如,日期格式、字符串长度限制)

代码示例 (Python):

import requests
import json
import hashlib

def create_auto_debit_request(merchant_id, merchant_user_id, amount, callback_url, salt_key, salt_index):
    """创建自动扣款请求"""

    payload = {
        "merchantId": merchant_id,
        "merchantUserId": merchant_user_id,
        "amount": amount,  # 确保是整数,单位是分
        "merchantOrderId": "order_" + str(int(time.time() * 1000)), # 使用一个不会重复的orderId
        "callbackUrl": callback_url,
        "expiry": {
            "type": "DATE",
            "value": "2024-12-31" # 设置一个合理的过期时间
         },
        "instrument":{
            "type": "TOKEN",  #通常对于定期扣款为token
             # 根据具体情况,这里需要填充其他信息,例如 card details 或 UPI details
        },
        "subscriptionType":"MONTHLY", #定期类型 MONTHLY or ON_DEMAND.

        # ... 其他必要参数 ...
    }
      #Phonepe请求需要先做base64编码,再做sha256加密。
    payload_string = json.dumps(payload, separators=(',', ':'))
    encoded_payload = base64.b64encode(payload_string.encode('utf-8')).decode('utf-8')

    # checksum的生成方式:  base64(sha256(encoded_payload + /v3/recurring/debit/init + salt_key) + ### + salt_index
    checksum_string = encoded_payload + "/v3/recurring/debit/init" + salt_key
    checksum_hash = hashlib.sha256(checksum_string.encode('utf-8')).hexdigest()
    checksum = checksum_hash + "###" + str(salt_index)

    headers = {
        "Content-Type": "application/json",
        "X-VERIFY": checksum, # 签名放在 X-VERIFY 头
         # ... 其他可能的请求头 ...
    }

    url = "https://api.phonepe.com/apis/hermes/v3/recurring/debit/init"  # 生产环境 URL
    #url = "https://api-preprod.phonepe.com/apis/pg-sandbox/v3/recurring/debit/init"   # 沙盒环境URL

    try:
        response = requests.post(url, headers=headers, data=json.dumps({"request": encoded_payload}))
        response.raise_for_status()  # 如果响应状态码不是 2xx,会抛出异常
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"请求出错: {e}")
        return None
    except Exception as e:
          print(f"处理响应出错:{e}")

# 示例调用
import time
import base64
merchant_id = "YOUR_MERCHANT_ID"
merchant_user_id = "user123"
amount = 10000  # 金额,单位是分
callback_url = "https://your-website.com/callback"
salt_key = "YOUR_SALT_KEY" #PhonePe分配的salt_key
salt_index = 1 # salt key index

response_data = create_auto_debit_request(merchant_id, merchant_user_id, amount, callback_url, salt_key, salt_index)

if response_data:
    print(f"响应数据:{response_data}")

安全建议:

  • 不要在代码中直接写死密钥 (salt_key),应从安全的地方(例如环境变量、密钥管理服务)读取。
  • 确保回调 URL (callbackUrl) 使用 HTTPS,保护数据传输安全。

2. 验证签名 (checksum)

原理: PhonePe 使用签名来确保请求的完整性和真实性。 签名算法通常是: 将请求数据、API 路径、salt key 拼接起来,进行 SHA256 哈希,然后加上 ### 和 salt key index。

操作步骤:

  • 仔细阅读 PhonePe 的签名文档,了解具体的签名算法。
  • 确保你用于生成签名的:
    • 请求数据与实际发送的请求数据完全一致。
    • API 路径正确 (在本例中是 /v3/recurring/debit/init)。
    • 使用了正确的 salt key 和 salt index。
  • 使用与 PhonePe 文档一致的哈希算法 (SHA256) 和拼接方式。

代码示例(Python,接上一节):

代码中的checksum变量的生成, 已经实现了签名生成逻辑,请参考上述Python示例代码。需要特别注意以下几点:

  1. base64编码 :PhonePe要求对请求体先进行base64编码。
  2. 数据排序 : 部分请求的json结构内key需要排序.(不是全部)
  3. 分隔符 : json.dumps()使用 separators=(',', ':') 来移除空格,确保生成的 JSON 字符串最紧凑.

进阶使用技巧:

可以编写一个独立的签名函数,方便在多个 API 调用中复用。 签名函数应接收请求数据、API 路径、salt key、salt index 作为参数,返回计算出的签名。

3. 检查请求头 (Headers)

原理: 确保 Content-Type 设置为 application/jsonX-VERIFY 头包含了正确的签名。

操作步骤:

  • 在代码中明确设置 Content-Typeapplication/json
  • 将上一步计算出的签名放在 X-VERIFY 头中。

代码示例 (Python,接上一节):

headers = {
    "Content-Type": "application/json",
    "X-VERIFY": checksum, # 签名放在 X-VERIFY 头
    # ... 其他可能的请求头 ...
}

已经包含在上述Python代码示例里。

4. 联系 PhonePe 支持

原理: 如果以上方法都试过了还是不行,很可能是 PhonePe 那边的问题,或者你的账户配置有问题。

操作步骤:

  • 准备好你的商户 ID (merchant ID)、错误信息、以及你发送的请求数据(脱敏后)。
  • 通过 PhonePe 提供的支持渠道(例如邮件、工单系统)联系他们,详细你的问题。
  • 询问他们你的服务器IP地址是否已经在白名单中。

5.检查编码

原理 : 确保编码一致性,可以减少出错可能性

操作步骤 :

  • 确认文件编码是UTF-8
  • 代码中编码设置与文档一致.

三、总结

解决 PhonePe API 报 "Incorrect Request" 错误,需要细心和耐心。从请求参数、签名、请求头这几个方面入手,逐一排查,通常都能找到问题所在。如果自己实在搞不定,及时联系 PhonePe 官方支持也是一个好办法。