PAYATHON 2026

如何处理通过 Square Register API 完成的付款退款?

支付小周

结论

Android Register API(后更名为 Square Point of Sale API)的付款结果通常只包含用于关联交易的 transactionId,不会直接返回能在 Square app 中搜索的 receipt ID。因此,收银员无法仅凭 Register API 的返回值,在 Square app 中按 receipt ID 找到交易并退款。

建议按以下方式处理:

  1. 保存 Register API 返回的 transactionId。
  2. 一并保存应用自己的订单号,以及金额、币种、门店等信息。
  3. 退款时,通过 Square 的服务端 API 查询交易并提交退款。
  4. 如果查询结果中有 receipt_number 或 receipt_url,可以展示给用户,但不要将其作为退款主键。

具体接口要根据所用的 Square API 版本确定。旧交易通常使用 Transactions/Connect API,新集成则应优先使用 Payments API 和 Refunds API。

为什么不能直接依赖 receipt ID

Register API 负责将付款请求交给 Square app,并在付款完成后把结果通知给调用方。返回的 transactionId 用于在 API 中关联交易,但 Square app 的收据搜索界面不保证能用这个 ID 搜索。

receipt number、receipt URL 和 transaction/payment ID 分别表示不同的信息:

  • transactionId 或 payment_id:用于通过 API 查询交易和退款。
  • receipt_number:向商户或顾客展示的收据编号。
  • receipt_url:在线收据地址。
  • tender_id:旧版 Transactions API 中某种付款方式的标识。

即使后续查到了收据编号,发起退款时仍应使用交易 ID 或付款 ID。

推荐处理流程

1. 保存付款回调中的交易 ID

Android 端收到 Square app 返回的结果后,应立即将 Square 交易 ID 与本地订单绑定:

String transactionId = data.getStringExtra("com.squareup.pos.TRANSACTION_ID");

order.setSquareTransactionId(transactionId);
order.setPaymentStatus("PAID");
orderRepository.save(order);

常量名称和结果解析方式会因 Square SDK 版本而异,应以当前版本的 SDK 定义为准,不要直接依赖示例中的字符串。

至少应保存以下字段:

local_order_id
square_transaction_id 或 square_payment_id
location_id
amount
currency
payment_status
created_at
refund_status

退款必须在服务端执行,不能将 Square access token 放入 Android app。

2. 查询对应交易

使用旧版 Transactions API 时,可以根据 Register API 返回的交易 ID 查询交易详情:

GET /v2/locations/{location_id}/transactions/{transaction_id}
Authorization: Bearer {ACCESS_TOKEN}
Square-Version: {SUPPORTED_VERSION}

示例:

curl \
  -H "Authorization: Bearer ${SQUARE_ACCESS_TOKEN}" \
  -H "Square-Version: ${SQUARE_VERSION}" \
  "https://connect.squareup.com/v2/locations/${LOCATION_ID}/transactions/${TRANSACTION_ID}"

通过查询结果可以确认:

  • 交易是否存在;
  • 交易所属门店;
  • 原始金额和币种;
  • 可退款的付款记录;
  • 旧退款接口所需的 tender_id;
  • 当前 API 是否返回可供展示的收据信息。

不要只按金额、时间或商品名称匹配交易,因为这些字段可能重复。

3. 通过旧版 Transactions API 退款

旧版退款接口通常需要提供 transaction_id、tender_id、退款金额和幂等键:

POST /v2/locations/{location_id}/transactions/{transaction_id}/refund
{
  "idempotency_key": "refund-order-8472-01",
  "tender_id": "TENDER_ID_FROM_TRANSACTION",
  "amount_money": {
    "amount": 1250,
    "currency": "USD"
  },
  "reason": "Customer returned item"
}

示例:

curl -X POST \
  -H "Authorization: Bearer ${SQUARE_ACCESS_TOKEN}" \
  -H "Square-Version: ${SQUARE_VERSION}" \
  -H "Content-Type: application/json" \
  "https://connect.squareup.com/v2/locations/${LOCATION_ID}/transactions/${TRANSACTION_ID}/refund" \
  -d '{
    "idempotency_key": "refund-order-8472-01",
    "tender_id": "TENDER_ID_FROM_TRANSACTION",
    "amount_money": {
      "amount": 1250,
      "currency": "USD"
    },
    "reason": "Customer returned item"
  }'

amount 使用货币的最小单位。例如,1250 美分表示 12.50 USD。

4. 新系统使用 Refunds API

如果查询或迁移后拿到的是 payment_id,应通过 Refunds API 退款:

POST /v2/refunds
{
  "idempotency_key": "refund-order-8472-01",
  "payment_id": "PAYMENT_ID",
  "amount_money": {
    "amount": 1250,
    "currency": "USD"
  },
  "reason": "Customer returned item"
}

Register API 返回的旧 transactionId 能否直接作为新版 payment_id 使用,取决于交易创建时间和 Square 的兼容映射。不能默认两者相同,应先通过对应的查询接口确认。

注意事项

  • 每次退款都要使用稳定且唯一的 idempotency_key,以免网络重试导致重复退款。
  • 退款币种必须与原付款一致,退款总额不能超过可退余额。
  • 一笔交易可能包含多个 tender。使用旧接口时,必须选择正确的 tender_id。
  • 应保存退款请求 ID、Square refund ID、退款金额和最终状态,不能只记录“已提交”。
  • 退款状态可能包括 PENDING、COMPLETED 或 FAILED,具体名称以当前 API 的返回值为准。
  • Square app 的搜索方式和 API 字段可能随版本变化。即使交易详情中包含 receipt_number 或 receipt_url,这些信息也更适合展示和人工核对,不能替代 transaction_id 或 payment_id。
  • 如果业务要求员工在 Square app 内手工退款,可以额外保存交易时间、金额、支付卡尾号等辅助信息。程序化退款仍应以 Square 的交易标识为准。

备注:内容仅供参考。