You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jazzcash支付网关集成中pp_SecureHash无效问题求助

JazzCash支付网关集成问题排查(NestJS)

问题代码

import axios from 'axios';
import crypto from 'crypto';

async initiatePayment(
  amount: number,
  billReference: string,
  description: string,
) {
  const txnRefNo = `T${Date.now()}`;
  const txnDateTime = new Date()
    .toISOString()
    .slice(0, 19)
    .replace(/[-:T]/g, '');
  const txnExpiry = txnDateTime; // For sandbox, same as txnDateTime

  const payload: Record<string, any> = {
    pp_Version: '1.1',
    pp_TxnType: 'MPAY',
    pp_TxnRefNo: txnRefNo,
    pp_MerchantID: this.merchantId,
    pp_Password: this.password,
    pp_Amount: amount.toString(),
    pp_TxnCurrency: 'PKR',
    pp_TxnDateTime: txnDateTime,
    pp_TxnExpiryDateTime: txnExpiry,
    pp_BillReference: billReference,
    pp_Description: description,
    pp_CustomerCardNumber: 5123450000000008,
    pp_UsageMode: 'API',
  };

  // Generate secure hash
  payload.pp_SecureHash = this.generateSecureHash(payload);

  try {
    console.log('payload:', payload);
    const response = await axios.post(this.baseUrl, payload);
    console.log('response.data:', response.data);
    return response.data; // JazzCash response
  } catch (err) {
    console.error('JazzCash API error:', err.response?.data || err.message);
    throw err;
  }
}

private generateSecureHash(payload: Record<string, any>): string {
  const sortedKeys = Object.keys(payload)
    .filter((k) => payload[k] !== undefined && payload[k] !== '')
    .sort();

  const dataString = sortedKeys.map((k) => `${k}=${payload[k]}`).join('&');
  const hash1 = crypto
    .createHash('sha256')
    .update(this.integritySalt + dataString)
    .digest('hex')
    .toUpperCase();

  const hash2 = crypto.createHash('sha256').update(dataString).digest('hex');

  const key = new Buffer('hi', 'hex');
  const hash4 = crypto
    .createHmac('sha256', key)
    .update(dataString)
    .digest('hex');

  const hash5 = crypto.createHash('sha256').update(dataString).digest('hex');

  console.log('hash1:', hash1);
  console.log('hash2:', hash2);
  console.log('hash4:', hash4);
  console.log('hash5:', hash5);

  return hash1;
}

错误响应

{
  "responseCode": "110",
  "responseMessage": "Please provide valid value for pp_SecureHash.",
  "authenticationStatus": null,
  "status": null,
  "gatewayRecommendation": null,
  "secureHash": "2BBA2766BB4988BF8848A0BFF37F62EFE024CBC544C24B34E851CDB221A48AC2"
}

问题解答

1. 版本选择:优先用v2.0

JazzCash目前官方推荐使用v2.0版本,v1.1虽仍兼容,但v2.0提供了更严谨的安全机制(HMAC-SHA256哈希)、更多功能支持,且后续v1.1可能逐步被淘汰。沙箱环境完全支持v2.0,建议切换到该版本,避免后续兼容性问题。

2. Payload字段顺序:不影响哈希验证,但需注意字段格式

JazzCash的哈希计算仅要求按字段名升序排序生成哈希串,请求发送时的字段顺序不影响结果。但你的payload存在两个潜在问题:

  • pp_CustomerCardNumber是数字类型,JazzCash要求该字段为字符串类型,数字序列化可能导致格式异常;
  • pp_Amount直接转字符串(如100转成"100"),但JazzCash要求金额必须是带两位小数的字符串(如"100.00"),格式不符会导致哈希计算错误。

3. 哈希计算排查方向(核心解决路径)

(1)排除pp_SecureHash字段参与计算

你当前的代码是先构造payload,再添加pp_SecureHash,然后传入generateSecureHash方法。但计算哈希时必须排除pp_SecureHash本身,否则会导致哈希串包含该字段,与网关验证逻辑不符。修改方式:在生成哈希前,先复制payload并移除pp_SecureHash字段,再进行排序计算。

(2)核对Integrity Salt正确性

沙箱和生产环境的Integrity Salt完全不同,必须使用JazzCash官方提供的对应环境的Salt值,不能自定义或混用环境值。

(3)严格遵循对应版本的哈希规则

  • v1.1规则:将所有非空必填字段按键名升序拼接为key=value&key=value格式,在字符串前拼接Integrity Salt,然后执行SHA256哈希,结果转大写;
  • v2.0规则:使用HMAC-SHA256算法,密钥为Integrity Salt,对排序后的键值串进行哈希。
    建议拿JazzCash官方文档中的示例数据,用你的代码计算哈希,对比文档给出的结果,快速定位逻辑错误。

(4)检查请求编码格式

axios默认发送JSON格式请求,但部分支付网关仅支持application/x-www-form-urlencoded编码。尝试将payload转为form-urlencoded格式发送:

import qs from 'qs';
// ...
const response = await axios.post(this.baseUrl, qs.stringify(payload), {
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});

(5)验证字段值格式

确认所有字段值符合JazzCash要求:

  • pp_TxnDateTime和pp_TxnExpiryDateTime必须是YYYYMMDDHHMMSS格式;
  • pp_Amount必须保留两位小数;
  • 所有字符串字段不能包含多余空格。

内容的提问来源于stack exchange,提问作者Muhammad Fuaad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 19:34:54