Alipay Payment Gateway 报错 ILLEGAL_PARTNER_EXTERFACE
结论
ILLEGAL_PARTNER_EXTERFACE 通常与 Django、支付表单或 payment_type 本身无关。这个错误表示支付宝认为当前商户没有权限调用请求中的支付接口。
建议先检查以下三项:
- 商户账号是否已签约并开通目标接口,例如旧版即时到账接口
create_direct_pay_by_user。 - 网关、商户身份和接口参数是否属于同一环境,沙箱配置与生产配置不能混用。
- 是否混用了旧版 Partner 接口与新版 OpenAPI 的
app_id、密钥或网关。
只修改 payment_type 通常无法解决接口权限问题。在旧版即时到账接口中,payment_type 一般为 1。沙箱和生产环境的区别主要在于网关地址、账号凭据、接口授权和签名配置,并不是换一个 payment_type 就能解决。
错误原因
EXTERFACE 是支付宝历史返回值中的拼写,应按原样识别。ILLEGAL_PARTNER_EXTERFACE 通常表示 Partner 没有目标接口的访问权限。
常见原因有:
- 请求使用了
create_direct_pay_by_user,但商户账号没有签约即时到账产品。 - 使用沙箱账号请求生产网关,或者使用生产 Partner 请求沙箱网关。
- 代码示例使用旧版 Partner API,但账号只开通了新版当面付、电脑网站支付或手机网站支付产品。
service参数指定了账号未开通的接口。- 合作伙伴身份参数有误,例如
partner、seller_id或收款账号填写错误。 - 签约主体、收款主体与请求中的商户身份不一致。
- 已签约产品尚未生效,或者商户后台显示的产品能力与代码调用的 API 不一致。
签名有误时,支付宝一般会返回签名相关的错误。因此,收到 ILLEGAL_PARTNER_EXTERFACE 后,应先排查接口权限和环境配置是否对应。
解决步骤
1. 确认实际调用的接口
检查提交给支付宝的参数,尤其是 service:
service=create_direct_pay_by_user
这个值对应旧版即时到账接口,当前 Partner 必须已开通该产品。即使商户开通了其他支付宝支付产品,也不会自动获得这个接口的调用权限。
如果接入的是新版电脑网站支付,通常应使用:
alipay.trade.page.pay
新版接口使用 app_id 和 RSA/RSA2 签名,不再采用旧版 partner + key 的 MD5 接入方式。两套协议的参数不能混用。
2. 检查环境是否完整匹配
同一次请求使用的所有配置都必须来自同一环境:
| 配置项 | 必须保持一致 |
|---|---|
| Gateway | 沙箱网关或生产网关 |
| 商户身份 | 对应环境的 partner 或 app_id |
| 密钥 | 对应应用和环境的密钥 |
| 接口权限 | 对应账号已开通的产品 |
| 回调地址 | 当前环境可以访问的地址 |
不能只替换 Gateway,却继续使用另一个环境的账号或密钥。
支付宝平台可能调整具体网关地址和沙箱能力,应以当前支付宝开放平台控制台中的配置为准,不要直接照搬多年前的示例地址。
3. 核对旧版请求参数
旧版即时到账请求通常包含以下参数:
params = {
"service": "create_direct_pay_by_user",
"partner": ALIPAY_PARTNER,
"_input_charset": "utf-8",
"payment_type": "1",
"notify_url": notify_url,
"return_url": return_url,
"seller_id": ALIPAY_PARTNER,
"out_trade_no": order_no,
"subject": subject,
"total_fee": amount,
}
其中,payment_type 一般是字符串 "1"。但即使所有参数都填写正确,只要 ALIPAY_PARTNER 没有 create_direct_pay_by_user 权限,支付宝仍会返回 ILLEGAL_PARTNER_EXTERFACE。
还要确认:
partner == 已签约接口的合作伙伴 ID
seller_id == 实际收款主体
部分历史接口支持卖家邮箱参数,但在没有弄清账号配置前,不要混用 seller_id、seller_email 和 Partner ID。
4. 在商户后台确认产品权限
登录对应的商户账号或开放平台账号,逐项检查:
- 已签约产品的准确名称;
- 产品状态是否有效;
- 产品对应的 API 名称;
- 应用是否已绑定该产品能力;
- 当前请求使用的是沙箱应用还是生产应用。
如果后台找不到目标接口的签约入口,可能是该历史产品已不再向新商户开放。此时应改用当前可以申请的电脑网站支付接口,不必继续调整旧版参数。
商户接口权限无法通过代码绕过。如果后台配置正确,但接口仍返回该错误,需要向支付宝技术支持提供 partner 或 app_id、接口名、请求时间和支付宝返回的请求标识,请其核查权限状态。工单和日志中不要提交商户私钥。
Django 中的推荐接入方式
新建的 Django 项目更适合使用支付宝 OpenAPI 的电脑网站支付接口 alipay.trade.page.pay。Python 社区常用的选择是 python-alipay-sdk,不过名称相近的包较多。安装前应核对源码地址、维护状态和文档,避免安装错误的同名包。
下面的代码采用常见的 SDK 接口形式。不同版本的初始化参数可能不同,请以实际安装版本的文档为准:
from alipay import AliPay
from django.conf import settings
from django.http import HttpResponseRedirect
alipay = AliPay(
appid=settings.ALIPAY_APP_ID,
app_notify_url=settings.ALIPAY_NOTIFY_URL,
app_private_key_string=settings.ALIPAY_APP_PRIVATE_KEY,
alipay_public_key_string=settings.ALIPAY_PUBLIC_KEY,
sign_type="RSA2",
debug=settings.ALIPAY_DEBUG,
)
def create_payment(request, order_no):
order = get_order_and_check_owner(order_no, request.user)
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order.number,
total_amount=f"{order.amount:.2f}",
subject=order.subject,
return_url=settings.ALIPAY_RETURN_URL,
notify_url=settings.ALIPAY_NOTIFY_URL,
)
gateway = (
settings.ALIPAY_SANDBOX_GATEWAY
if settings.ALIPAY_DEBUG
else settings.ALIPAY_PRODUCTION_GATEWAY
)
return HttpResponseRedirect(f"{gateway}?{order_string}")
不要根据 debug 自行推测网关地址。建议在配置中明确保存控制台或 SDK 文档提供的地址:
# settings.py
ALIPAY_APP_ID = "your-app-id"
ALIPAY_DEBUG = True
ALIPAY_SANDBOX_GATEWAY = "sandbox-gateway-from-current-documentation"
ALIPAY_PRODUCTION_GATEWAY = "production-gateway-from-current-documentation"
私钥不应直接提交到 Git 仓库,可以通过环境变量或密钥管理服务加载。
异步通知验证
不能仅凭浏览器跳转到 return_url 就认定支付成功。服务器应通过 notify_url 接收支付宝通知,完成验签并核对金额和订单号后,再更新订单状态。
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def alipay_notify(request):
data = request.POST.dict()
signature = data.pop("sign", None)
if not signature or not alipay.verify(data, signature):
return HttpResponse("failure")
order_no = data.get("out_trade_no")
trade_status = data.get("trade_status")
total_amount = data.get("total_amount")
order = get_order_for_update(order_no)
if not amount_matches(order.amount, total_amount):
return HttpResponse("failure")
if trade_status in {"TRADE_SUCCESS", "TRADE_FINISHED"}:
mark_order_paid_idempotently(order, data.get("trade_no"))
return HttpResponse("success")
生产代码还需要校验通知中的 app_id、收款方身份、订单币种等字段,并通过数据库事务和幂等逻辑处理重复通知,避免重复入账。
注意事项
payment_type="1"只是特定旧接口中的业务参数,不代表商户拥有该接口的调用权限。- 不要混用旧版 MD5 Partner API 的
partner、key与新版 RSA2 OpenAPI 的app_id、应用私钥。 return_url是用户浏览器的跳转地址,不能作为判断支付成功的唯一依据。notify_url必须能被支付宝服务器访问,本机localhost地址通常无法接收通知。- 金额应使用
Decimal,不要直接用二进制浮点数计算订单金额。 - 日志可以记录接口名、订单号、运行环境和支付宝请求标识,但不能记录私钥、完整签名材料或其他敏感凭据。
- 如果项目依赖的工具包只支持
create_direct_pay_by_user,而商户实际开通的是alipay.trade.page.pay,应更换或升级工具包,不要继续修改payment_type。
备注:内容仅供参考。
