关于 israeli-payment-orchestrator
用于设计以以色列支付网关为核心的支付编排技能,涵盖能力比较、路由、分期、金额单位规范化,以及安全重试与对账。
主要功能
- 01比较网关能力
- 02统一不同网关 API 的金额单位
- 03按功能与健康状态路由支付
- 04建模分期类型
- 05设计故障转移与对账逻辑
适用场景
- 01按所需支付方式选择网关
- 02网关故障时路由交易
- 03避免超时后重复扣款
- 04为多个网关构建统一适配层
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ skills-il/ tax-and-finance --skill israeli-payment-orchestrator安装后在 Claude Code 中输入/israeli-payment-orchestrator确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evidence.jsonmetadata.jsonoptimization-log.jsonSKILL_HE.mdreferences/domain-checklist.mdgateway-matrix.mdscripts/compare_gateways.py详细介绍
以色列支付编排器
说明
第 1 步:评估支付需求
询问用户的支付需求:
| 需求 | 希伯来语 | 说明 | 对网关选择的影响 |
|---|---|---|---|
| 分期付款(tashlumim) | תשלומים | 将付款拆分为每月分期付款 | 并非所有网关都支持所有分期付款类型 |
| Bit / Apple Pay | ביט / אפל פיי | 替代支付方式 | 取决于网关的集成方式 |
第 2 步:比较网关
使用 scripts/compare_gateways.py 生成比较矩阵,或参考下表:
| 网关 | API 风格 | 分期付款 | 定期付款 | 托管页面 | Bit | 金额字段单位 |
|---|---|---|---|---|---|---|
| Cardcom | REST JSON | 普通、信用卡 | 是 | 重定向或 iframe(Low Profile) | 是(UrlToBit) | 主要单位(小数) |
| Tranzila | 表单编码 CGI(旧版)+ REST JSON(API V2) | 普通、信用卡 | 是 | iframe、重定向或托管字段 | 是(专用 Bit API;iframe 上的 bit_pay=1) | 主要单位(小数) |
| PayMe | REST JSON | 未验证 | 未验证 | 未验证 | 未验证 | 未验证,请先确认 |
| Meshulam (Grow) | multipart/form-data | 普通、信用卡 | 是 | iframe + 重定向 | 是(存在 cancelBitTransaction) | 主要单位(小数) |
| iCredit | WCF .svc 服务 | 未验证 | 未验证 | 未验证 | 未验证 | 未验证,请先确认 |
| Pelecard | REST JSON | 普通、信用卡 | 是 | 重定向或 iframe | 是,参见 pelecard-payment-gateway | 次要单位(agorot) |
关于定价:此技能不公布费率区间。 这六家网关都没有公布费率表。商户折扣率由合同约定,并根据商户的交易量、行业和拒付情况单独报价;此技能的早期版本曾列出各网关的费率区间,但没有供应商愿意为这些数据背书。基于编造数字指定最便宜网关的比较表毫无依据,而且只要差异小于任何实际谈判可能产生的差异,排名就会颠倒。请让用户向各网关索取书面报价。
“未验证”不等于“不支持”。 如果没有公开来源能够证实某项功能是否可用,此技能会如实说明,而不会猜测其不支持。PayMe 和 iCredit 的大多数项目均未验证,这并非因为它们不具备这些功能,而是因为两者都没有发布可访问的 API 参考文档。涉及 PayMe 或 iCredit 的每项设计决策,都应先向供应商确认。
各网关专属技能是对应网关的权威资料。 关于字段名称、错误代码、身份验证流程和端点路径,请以 cardcom-payment-gateway、tranzila-payment-gateway、grow-payment-gateway 和 pelecard-payment-gateway 为准。PayMe 和 iCredit 没有专属技能,因此它们的多个单元格标为“未验证”。
第 3 步:设计编排层
构建统一的支付抽象:
# Unified payment interface pattern
class PaymentRequest:
action: str # פעולה - "sale" (charge now) or "authorize" (hold now,
# capture later). Two different gateway calls, see Step 7.
amount: int # סכום - store ONE canonical unit, agorot, as an integer
# and convert per gateway on the way out (see below)
currency: str # מטבע - "ILS" default
installments: int # תשלומים - 1 = regular, 2-36 = installments
installment_type: str # סוג תשלומים - "regular", "credit", "club"
card_token: str # טוקן כרטיס - for recurring
description: str # תיאור עסקה
customer_id: str # מזהה לקוח
idempotency_key: str # מפתח אידמפוטנטי - prevent duplicates
class PaymentResult:
outcome: str # תוצאה - "approved" | "declined" | "unknown"
# NOT a bool: "unknown" is a real third state, see Step 6
gateway_used: str # שער תשלום שנבחר
transaction_id: str # מזהה עסקה
approval_number: str # מספר אישור
shva_reference: str # מספר שב"א
gateway_status_code: str # קוד סטטוס גולמי - keep the vendor's own code, not just
raw_response: dict # the normalised outcome, so a parked or mis-mapped
# transaction stays auditable afterwards
installment_details: dict
在适配器中统一金额单位,绝不要在调用方处理。 各网关的金额字段单位并不一致:Pelecard 使用 agorot(次要单位),而 Grow 使用谢克尔小数金额。各网关专属技能(pelecard-payment-gateway、grow-payment-gateway)分别是对应网关的权威资料;PayMe 和 iCredit 没有专属技能,也没有可访问的公开文档,因此在正式上线前,请先用 1 谢克尔进行测试扣款,以确认它们的金额单位。在编排器内部使用单一的标准整数单位,并在各网关适配器中进行转换。如果各适配器使用的单位相差 100 倍,却共用 float 金额,就会在两个方向上产生错误,而且问题会一直悄无声息,直到客户投诉。
步骤 4:实现网关路由
定义路由规则,以选择最优网关:
| 规则 | 优先级 | 逻辑 | 示例 |
|---|---|---|---|
| 撤销与结算成本 | 中 | 根据商户实际能够获知的成本进行路由,这些成本来自商户自己的合同 | 对于可能被取消的扣款,将其路由到批次开放时间最长的网关,因为撤销不会清算任何款项,而退款会清算两次 |
| 功能匹配 | 高 | 根据所需功能进行路由 | 将符合 Bit 条件的销售路由到已启用 Bit 的终端所在网关 |
| 可用性 | 关键 | 将流量从故障或性能下降的网关移开 | 如果 Tranzila 宕机,则故障转移到 Cardcom |
| 流量均衡 | 低 | 在多个网关之间分配负载 | 主网关与次网关按 60/40 分配 |
| 卡类型 | 高 | 某些网关对特定卡的处理更好 | Diners Club 路由 |
路由逻辑:
def select_gateway(request: PaymentRequest, gateways: list) -> str:
# 1. Filter by feature support (tashlumim type, Bit, etc.)
eligible = [g for g in gateways if g.supports(request)]
# 2. Remove unhealthy gateways
healthy = [g for g in eligible if g.is_healthy()]
# 3. Sort by cost for this transaction type
ranked = sorted(healthy, key=lambda g: g.priority_for(request))
# Rank on merchant-configured priority, NOT on a fee table.
# No Israeli gateway publishes rates, so a "cheapest gateway" sort
# can only be built on invented numbers. See Step 2.
# 4. Return best match (or raise if none available)
if not ranked:
raise NoGatewayAvailable()
return ranked[0]
步骤 5:处理分期付款(Tashlumim)
分期付款模式通过 CreditType 字段传递(在 Tranzila 的旧版接口中为 cred_type),它与交易类型(סוג עסקה)不同。
所有供应商公开的 CreditType 值只有三种。 Tranzila 当前的参数表恰好列出了这三种(1 一次付款、6 信用分期、8 分期)。Cardcom 的 v11 API 架构将 CreditType 默认设为 1,并且只明确列出 6(“6 - israel credit for multiple payments”)。
| 类型 | 希伯来语 | CreditType | 工作方式 | 谁支付利息 |
|---|---|---|---|---|
| 单次付款 | תשלום אחד | 1 | 立即扣款一次 | 不适用 |
| 信用分期 | קרדיט | 6 | 发卡机构提供融资;客户分期还款并支付利息 | 客户向发卡机构支付利息 |
| 常规分期 | תשלומים | 8 | 商户收到全额,发卡机构每月向客户收款 | 默认情况下客户不支付利息 |
| “免息分期” | תשלומים ללא ריבית | 8 | 与常规分期使用相同的线缆值;商户以商业方式承担融资成本 | 商户承担成本 |
不要发送 2、3、4、5 或 9。 此技能的早期版本根据第三方集成库,而非供应商文档,将这些值列为“规范枚举”。当前供应商参数表中没有这些值,Cardcom 的架构也没有包含其中任何值;Tranzila 自己的参数表只公开了 1、6 和 8,其响应代码表对其他任何值均返回 017 Unauthorized credit type for this transaction。尤其是,不存在可路由的“俱乐部分期 = 9”:俱乐部和发卡机构忠诚度计划属于终端和发卡机构配置,而不是代码可选择的 CreditType。如果商户需要此功能,应与收单机构沟通,而不是设置字段值。
实现说明:
- 分期上限属于终端和发卡机构配置,并非网关常量。应向收单机构询问具体终端的上限,而不要将其硬编码。
- 发卡机构信用分期(CreditType 6)有自己的最低分期数限制。Tranzila 文档规定信用分期至少为 3 期,并指出持卡人必须拥有发卡机构提供的信用授权,因此信用分期交易可能因与网关无关的原因失败。每期最低金额由发卡机构和终端配置;各网关技能会提供这些信息,不要在此处硬编码。
- “免息分期”是与收单机构之间的商业安排,而非线缆值:客户无需支付利息,因为融资成本由商户承担。不要报出补贴比例,该比例因商户而异,且未公开。
- API V2 接口可能完全不使用
cred_type。例如,Tranzila 使用payment_plan和installments_number。在照搬参数名称之前,先确定所使用的接口。
步骤 6:实现回退和重试逻辑
设计具有韧性的支付处理流程:
# Fallback strategy
GATEWAY_PRIORITY = ["cardcom", "tranzila"] # documented gateways only, see below
async def process_with_fallback(request: PaymentRequest) -> PaymentResult:
last_error = None
for gateway_name in GATEWAY_PRIORITY:
gateway = get_gateway(gateway_name)
if not gateway.is_healthy():
continue # דלג על שער לא זמין
try:
result = await gateway.charge(request)
if result.outcome in ("approved", "declined"):
return result # both are settled answers, stop here
# outcome == "unknown": the charge MAY have gone through.
# Do not fail over. Park it for reconciliation.
await park_for_reconciliation(request, gateway_name)
raise OutcomeUnknown(gateway_name)
except GatewayTimeoutError:
# A timeout is NOT a failure. It is an unknown outcome.
await park_for_reconciliation(request, gateway_name)
raise OutcomeUnknown(gateway_name)
except GatewayConnectFailed as e:
# The request never reached the gateway, so nothing was charged.
last_error = str(e)
continue # נסה שער הבא
raise AllGatewaysFailedError(last_error)
有三条规则,第三条会造成经济损失:
- 银行拒绝(余额不足、卡片被盗)时,绝不要改用其他网关重试。结果不会改变,而且速度检查会开始标记这张卡。
- 仅当有确凿证据表明请求从未到达网关(DNS 故障、连接被拒绝、TLS 故障)时才重试。
- 绝不要将未经验证的网关加入故障转移链。 只有确认以下三件事后,网关才能列入优先级列表:金额字段使用什么单位、哪个字段用于传递你的幂等键,以及如何撤销该网关上的扣款。若转移到一个金额单位尚未确认的网关,回退路径本身就会变成金额扩大 100 倍的 bug。
- 超时不等于失败。 它是第三种结果;将其故障转移是经典的重复扣款 bug。Pelecard 通过自己的状态码明确区分了这些情况:
302表示“扣款成功,但商户无响应”,也就是说钱确实已经划转;301和303则表示结果确实未知。只有302确认发生了扣款。若编排器将这三种状态都映射为“失败,尝试下一个网关”,那么每个首次尝试响应较慢的客户都会被重复扣款。先暂存交易,使用网关自己的查询接口核实,然后再做决定。
暂存只是工作的一半;上线重试前,先设计好对账流程。 park_for_reconciliation 不是留待以后填写的存根,它负责将未知结果转化为资金或商品。以下三件事必须在首次扣款前就绪,而不是等到第一次事故发生后:
- 在调用前持久化一个密钥,以便之后通过它查询交易。 请求超时时,网关自己的交易 ID 还不存在,因此不能用它作为该密钥。使用由你控制的透传字段。这里有两个规则相反的不同字段,混淆它们本身就会导致重复扣款。 关联标识符用于标识一次尝试,因此每次尝试都必须唯一;对账时按订单 ID 前缀匹配:Pelecard 的
ParamX就是这样使用的,例如<order-id>-<attempt-n>。幂等键用于让网关去重,因此同一笔逻辑扣款必须保持稳定,并在每次重试时原样重新发送:Grow 的transactionUniqueIdentifier就是这样使用的;每次尝试都生成新的值,会破坏 Grow 自己的重复检测机制。生成密钥前,请先在各网关对应的技能说明中确认该字段属于哪一种。 - 重试时使用相同的密钥。 超时或 5xx 错误后,使用新的幂等键重新发送请求,正是导致客户被扣两次款的原因。请原样重发同一个密钥,或先完成对账。
- 针对每个网关的查询方式、轮询时限和终态决策(扣款并发货、退款,或升级给人工处理)。
references/gateway-matrix.md列出了各网关的查询调用。在时限结束后仍然未知的结果属于运维任务,必须有人负责处理。Pelecard 对这三种状态的原文说明可通过/services/GetErrorMessageEn查询,无需凭据,参数为errorCode;未分配的代码会返回 Pelecard 拼写错误的UnkownError,这可以用来区分真实代码和臆造的代码。在编写任何重试逻辑之前,请根据每个目标网关自己的代码表,为其建立等效的三状态映射。
步骤 7:处理授权、撤销、退款和部分退款
授权不等于销售,撤销授权也不等于退款。 这些支付网关大多支持两步流程:现在冻结金额,稍后再扣款(Pelecard ActionType J5、Tranzila verify_mode=5 / tranmode=V、Cardcom Operation: SuspendedDeal)。忽略这一点会导致两个问题。编排器将授权记录为“已批准”后,如果从未发起扣款,就收不到款项,而冻结也会悄然过期。编排器如果用退款调用来解除冻结,就是调用了错误的操作:Tranzila 使用 txn_type=reversal 而不是 cancel 来释放额度;Pelecard 使用 /services/DeleteIshur 而不是 DeleteTran 来释放额度。在请求中携带 action(步骤 3),并在适配器中提供 capture() 和 release(),与 void() 和 refund() 并列。各支付网关对应的调用见 references/gateway-matrix.md。
对已扣款项进行冲正,是编排器独有的职责,也是各支付网关之间差异最大的操作。这两种操作在机制上截然不同,选错会让商家承担双重成本。
- 撤销(ביטול)会在交易结算前取消交易。交易不会进入清算网络,因此不产生清算费用,客户通常也不会看到扣款。
- 退款 / 贷记(זיכוי)是针对已结算扣款发起的一笔新的反向交易。该交易会单独进行清算,因此商家需要为两个方向的交易都支付清算费用。
分界点是结算,而结算来得很快:大多数终端会在同一营业日关闭批次。在忽略这一点的情况下,按成本优化的路由器(步骤 4)优化的只是账本的一半。
| 支付网关 | 撤销,结算前 | 退款 / 贷记,结算后 | 部分退款 |
|---|---|---|---|
| Cardcom | 使用 CancelOnly: true 调用 Transactions/RefundByTransactionId(“仅取消,且须在交易入账前”) | 调用同一端点,但不带 CancelOnly | 支持,使用 PartialSum。AllowMultipleRefunds 默认为 false |
| Tranzila | 在 API V2 中使用 txn_type=cancel | 旧版 tranmode=C{index}(需要单独的 CreditPass 凭据);在 API V2 中使用 txn_type=credit | 支持 |
| PayMe | 未验证 | 存在 api/refund-sale | 未验证 |
| Meshulam (Grow) | 不支持银行卡撤销。仅 Bit 支持:cancelBitTransaction,需要 pageCode;请在 grow-payment-gateway 中确认完整参数集 | refundTransaction,金额字段为 refundSum,不接受 sum | 支持,但交易结算或传输后会被阻止;资金到达银行后则需要人工批准。Grow 会为每种情况返回各自的错误代码;见 grow-payment-gateway |
| iCredit | 未验证 | 未验证 | 未验证 |
| Pelecard | 在批次仍开放时调用 /services/DeleteTran。/services/GetTransDataBeforeBc 会显示哪些交易仍可撤销 | /PaymentGW 上没有退款端点。贷记是一笔新的反向交易;不要猜测其路径 | 不支持。DeleteTran 只能针对整笔交易操作 |
需要考虑以下三点:
- **切勿假设操作对称。**退款调用并不是在扣款调用上加一个负号。尤其是 Pelecard,发送负数总额更可能导致再次扣款,而不是撤销交易。
- 所有支持 Bit 的支付网关都应使用单独的 Bit 退款流程,因为 Bit 是账户间主动转账,而非银行卡授权。
- **表格中的“未验证”单元格代表真实的信息缺口,并不表示默认不支持。**对于 PayMe 和 iCredit,请先向供应商确认,再基于假设构建撤销流程。
- **为每笔交易维护自己的累计退款账本,并在调用前拒绝超额退款。**各支付网关在是否阻止超额退款方面并不一致:Cardcom 默认将
AllowMultipleRefunds设为 false,而 Grow 接受连续部分退款,直到累计金额超过原金额。依赖支付网关拦截意味着六家中总有一家做不到。 - **退款往往需要扣款时不需要的第二项凭证。**Tranzila 的旧版信用退款路径需要单独的
CreditPass,且必须在终端上启用;Cardcom 的RefundByTransactionId则需要ApiPassword,而其扣款端点完全不需要此凭证。未经端到端测试的撤销流程通常恰恰会在这里失败。 - **分期销售的退款因收单机构而异,不能简单按期数均分金额。**发卡机构是否会停止收取剩余款项,以及商家已收取的全额款项如何追回,都会因收单机构而异。对于分期付款交易,上线退款流程前务必确认这些情况。
- 退款期限是法律规定的,而非支付网关规定的,且义务在于发起退款,而非确保客户收到退款。由于入账退款按发卡机构自己的周期结算,排队等待处理的退款必须受到监控,不能发起后就置之不理。
pelecard-payment-gateway提供了有来源依据的《消费者保护法》远程销售规则摘要;请将其视为摘要,而非法律原文。
运行 python scripts/compare_gateways.py --refunds 可打印此矩阵。
第 8 步:确保符合法规要求
遵守以色列银行和 Shva 的法规:
| 法规 | 希伯来语 | 要求 | 影响 |
|---|---|---|---|
| 交易数据保留 | שמירת נתוני עסקאות | 在法定期限内保留交易记录 | 保留期限由税务和簿记法律规定,而非支付网关或 PCI 规定。设计清理策略前,请向税务局或商家的会计师确认当前期限;不要直接照搬集成指南中的数字 |
| PCI DSS | תקן PCI | 卡数据安全 | 使用令牌化,切勿存储完整卡号。以色列收单机构可能会在国际标准基础上增加本地要求 |
| 消费者保护 | הגנת הצרכן | 远程销售中的法定取消权 | 取消期限、退款期限、取消费用的任何上限,以及特定消费者群体是否享有更长的期限,均由《消费者保护法》及其条例规定,而非支付网关。本技能特意不编码其中任何规定。围绕这些规定构建退款流程前,请向消费者保护局或法律顾问逐项确认 |
| 反欺诈 | מניעת הונאה | 3D Secure、频率检查 | 为 CNP 交易实施 3DS2 |
示例
示例 1:多支付网关配置
用户说:“我需要支持分期付款,并在某个支付网关宕机时进行故障转移” 操作:
- 评估:分期付款(常规和信用)以及高可用性。
- 比较:运行
python scripts/compare_gateways.py --features installments,recurring。选择两个单元格均有文档记录、而非未验证的支付网关。 - 设计:统一的金额单位、映射到各支付网关自身字段的幂等键,以及三态结果。
- 实施:将分期销售路由至主支付网关,仅在能够确认连接失败时才进行故障转移。 结果:具备主备路由和分期处理功能的编排层
示例 2:添加 Bit
用户说:“我们使用 Tranzila,但想添加 PayMe,以便接受 Bit” 操作:
- 首先质疑这个前提。Tranzila 已通过专用的 Bit API 和 iframe 上的
bit_pay=1标志支持 Bit。Cardcom(UrlToBit)、Grow 和 Pelecard 也支持。为了获得当前网关已有的功能而添加整个第二个网关,会带来大量集成工作,却毫无必要。 - 检查终端,而不是网关。收单机构必须在商户的终端上启用 Bit。“API 支持”与“你的终端已开通此功能”是两个不同的问题,真正会造成阻碍的是后者。
- 在将交易路由到 Bit 之前,先从收单机构和对应网关的技能文档中确认 Bit 自身的限制,而不是依赖此表。Bit 是账户到账户的推送支付,因此适用的限制与银行卡不同:预计会有分期付款、币种、单笔交易金额和月交易金额方面的限制,其撤销路径也可能与银行卡不同。请获取该商户自身终端的实际数值。把 Bit 当作布尔值处理的路由器,会发送 Bit 无法处理的交易,并在最糟糕的时刻失败。
- 只有在终端确实无法开通 Bit 时,才需要第二个网关;这时做出的就是商业决策,而非技术决策。 结果:大多数情况下,只需打一次电话开通,而不必进行第二次集成
示例 3:成本比较
用户说:“对于我们每天 500 笔、平均金额为 200 NIS 的交易,哪个网关最便宜?” 操作:
- 明确说明,无法根据公开信息回答这个问题。六家网关都没有公布费率表,费率要根据交易量、行业和拒付情况逐个商户协商。任何直接列出胜出者的表格都是在捏造输入数据。
- 提供真正决定结果的信息:用于协商的月交易额,此处为 500 * 30 * 200 = 3,000,000 NIS,并建议在该交易量下向每家网关索取书面报价。
- 列出折扣费率掩盖的成本,这些成本在这种规模下通常影响更大:由商户承担的免息分期利息、结算周期的营运资金成本、每笔退款和拒付的费用,以及先扣款再退款的组合会结算两次,而撤销则一次也不结算。
- 对于可明确了解的能力,进行比较:
python scripts/compare_gateways.py --example。 结果:获得谈判筹码和能力比较,而不是错误的排名
附带资源
脚本
scripts/compare_gateways.py-- 打印六家网关的能力矩阵,以及撤销/退款/部分退款矩阵。根据有文档记录的功能进行筛选;遇到无法识别的功能名称时会拒绝执行,而不是静默匹配所有内容。此脚本有意不估算成本。运行:python scripts/compare_gateways.py --help、--example、--refunds、--features bit
参考资料
references/gateway-matrix.md-- 跨网关比较:API 格式和基础 URL、身份验证方案、分期付款支持、Bit 和钱包支持、金额单位、授权/捕获/释放调用、撤销/退款/部分退款矩阵、用于对账的各网关交易查询方式,以及决定资金是否转移的 Pelecard 状态码。此文档有意不包含费用表。在评估或切换网关时请查阅。
参考链接
| 来源 | URL | 检查内容 |
|---|---|---|
| Cardcom v11 API schema | https://secure.cardcom.solutions/swagger/v11/swagger.json | 权威字段列表:CreditType 的取值、UrlToBit,以及包含 PartialSum 和 CancelOnly 的 RefundByTransactionId 请求 |
| Tranzila 文档 | https://docs.tranzila.com/ | 确认你使用的是四种接口中的哪一种、已发布的 cred_type 取值,以及 API V2 的四个请求头 HMAC 身份验证 |
| PayMe | https://payme.io/ | PayMe 未公开可直接访问的开发者文档 URL(payme.io/developers 会通过 301 重定向到主页,docs.paymeservice.com 则返回 Cloudflare 1014)。请直接向 PayMe 索取 API 参考资料,并在首次扣款前与其确认金额单位和退款参数名称 |
| Meshulam (Grow) 参考资料 | https://developers.grow.business/reference/overview | 生产环境基础 URL 为 secure.meshulam.co.il、退款时使用 refundSum,以及限制部分退款的错误代码 |
| Rivhit (iCredit) | https://www.rivhit.co.il/ | iCredit 是 Rivhit 的产品。其 API 是 WCF .svc 服务;退款接口未公开文档 |
| Pelecard 服务索引 | https://gateway21.pelecard.biz/services | 实际使用的端点列表,包括 DeleteTran 和 GetTransDataBeforeBc。gateway20 和 gateway21 代表不同代际,并非不同环境 |
| 以色列银行:支付系统监督 | https://www.boi.org.il/en/economic-roles/supervision-and-regulation/payment-systems-oversight/ | 哪些系统属于“受监管的支付系统”,因而处于以色列银行的监管范围内;以及监管指令和接入支付系统的要求。请注意,此页面并未明确由谁监管非银行支付服务提供商;应直接向监管机构确认,不要据此自行推断 |
注意事项
- 在上述任何一家网关中,HTTP 200 都不代表付款成功。 Tranzila 在其响应代码表顶部明确说明:这些是应用级错误代码,而响应代码会是成功(200)。Grow 会返回 200,同时包含
status: "0"和一个err对象。Pelecard 的init会返回 200,同时包含Error.ErrCode。Tranzila 的旧版 CGI 则会返回一个 HTML 提示页面,而非 JSON。规范化这些响应是编排器的职责;Agent 编写的集成最常见的错误,就是将失败的扣款报告为已付款。应解析响应正文,绝不要只看状态码。 - 此技能中的每家以色列支付网关(Cardcom、Tranzila、PayMe、Meshulam、iCredit、Pelecard)都有完全不同的 API 格式:Cardcom 使用 JSON;Tranzila 在旧版接口上使用表单编码的键值对,在 API V2 上使用 JSON;Meshulam 使用 multipart/form-data,并通过单独的页面代码参数;iCredit 则是 WCF
.svc服务。Agent 可能会把一家网关的格式套用到另一家。 - 主机名不是环境切换开关。 对于 Pelecard,
gateway20和gateway21是不同代的网关,返回的响应逐字节相同;决定调用是测试还是实际扣款的是终端编号和凭据。如果 Agent“切换到沙盒主机”后使用测试数据,就会产生真实扣款。 - 幂等键背后必须对应网关字段。 每家网关都有自己的重复检测机制和重复错误代码。如果编排器生成的键从未映射到网关自身的字段,那它就只是摆设:网关会欣然接受同一笔扣款两次。在 Grow 上,重复检测字段是
transactionUniqueIdentifier;对同一笔逻辑扣款的重试,该字段必须保持不变。在 Pelecard 上,透传参数ParamX是每次尝试的关联标识,而不是去重键;去重依赖返回的交易 ID。两者不可互换。对于其他网关,在依赖重试安全性之前,应查阅对应网关的技能,确认相关字段和重复错误代码。 - 以色列的支付处理要求企业在以色列注册(osek murshe/patur)。Agent 可能会在确认企业已向税务局完成适当注册之前,就建议开通支付处理。
- 在大多数网关中,Bit(以色列主流移动支付)的退款接口与信用卡退款接口不同。Agent 可能会对 Bit 交易使用信用卡退款接口。
- 已保存的卡令牌无法跨网关通用。 Cardcom 令牌、Tranzila 的
TranzilaTK和 PayMe 的buyer_key都是专有令牌,彼此不兼容,且只能用于签发该令牌的网关。这意味着此技能中的备用/故障切换模式仅适用于首次扣款和一次性交易。令牌化扣款或定期扣款无法切换到第二家网关;定期扣款只能在发起扣款的网关上进行。若要真正实现已保存卡片的跨网关容错,需要网络令牌化或多保险库策略,而不是使用此令牌字段。Agent 可能会错误地认为,已存储的令牌可用于优先级列表中的任何网关。 - 网关客户端侧的“成功”状态始终可能被伪造,务必在服务端重新验证。 不要相信浏览器重定向或客户端提交的状态能证明付款成功。履行订单前,应通过服务器到服务器查询向发卡网关确认每笔交易(对于 Meshulam/Grow,使用
getPaymentProcessInfo/approve;对于其他网关,使用网关的交易查询,或验证 webhook 签名/通知)。必须按照各网关各自的机制验证 webhook/IPN 载荷,不能互换使用。 - 3DS2 是异步挑战流程,不是一次调用。 无卡交易可能返回“无感验证”(内联批准),也可能触发挑战,将客户重定向到发卡机构,然后通过回调 URL 返回。编排器必须持久化交易信息,在回调时恢复流程,并确保挑战返回仍由发起挑战的同一家网关处理(3DS 会话限定于特定网关)。Agent 可能会将 3DS 建模为同步布尔值,导致挑战中的交易丢失。
故障排查
错误:"不支持此分期类型" / Tranzila 017
原因:通常是 CreditType 值本身有问题,而非网关问题。所有供应商公布的取值只有 1、6 和 8;2、3、4、5 和 9 虽然在第三方库中流传,但目前没有任何供应商的参数表列出这些值,Tranzila 会对它们返回 017 Unauthorized credit type for this transaction。
解决方案:常规分期请发送 8,由发卡机构提供融资的信用则发送 6。如果商户确实需要俱乐部或忠诚度计划,这属于终端和收单机构配置,而非字段值,因此应将请求转交给收单机构,而不是改用其他数字重试。已验证的否定结论:更换网关无法解决被拒绝的 CreditType,因为该值在到达网关之前就已被拒绝。
错误:"故障转移时网关超时"
原因:要么所有已配置的网关都处于性能下降状态,要么(更常见得多)某个网关超时,而编排器将其视为失败并转向下一个网关。 解决方案:区分这两种情况。超时意味着结果未知,应暂停处理以便对账,而不是故障转移(步骤 6)。只有能够确认的连接失败才足以转向下一个网关。明确这一区别后,再添加带健康检查的断路器,并在性能下降时发出警报。
错误:"检测到重复交易"
原因:重试逻辑重复发送了同一笔付款,通常是因为将超时当成了失败。 解决方案:将你的幂等键映射到网关自身的重复检测字段,并将超时视为结果未知,而非失败(步骤 6)。重试任何操作前,先查询该笔交易。切勿重试银行拒绝的交易。
错误:退款调用返回成功,但客户表示款项并未退回
原因:最可能的原因(并非已确认适用于所有网关)是退款金额已进入队列但尚未完成,或对已结算的交易执行了撤销。已验证的否定结论:在 Pelecard 上,这并非 /PaymentGW 退款端点故障,因为该端点并不存在。
解决方案:选择调用方式前,先确认原交易处于结算的哪一阶段(步骤 7),并通过网关自己的交易查询进行对账,而不要依赖撤销调用本身的响应。