From 783999f3965723c951c97b5f7cd966adccfefc0f Mon Sep 17 00:00:00 2001 From: yml Date: Wed, 3 Jun 2026 18:51:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B9=90=E5=88=B7=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E4=B8=8E=E9=92=B1=E5=8C=85=E5=85=85=E5=80=BC=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integrations/payment/leshua/client.go | 59 +- .../payment/leshua/client_test.go | 76 +- backend/internal/modules/payment/handler.go | 14 +- .../internal/modules/payment/repository.go | 75 +- backend/internal/modules/payment/service.go | 8 +- backend/internal/modules/wallet/service.go | 4 +- docs/乐刷商户交易.md | 1699 +++++++++++++++++ docs/乐刷支付接入文档.md | 3 +- frontend/src/views/account/WalletView.vue | 73 +- 9 files changed, 1938 insertions(+), 73 deletions(-) create mode 100644 docs/乐刷商户交易.md diff --git a/backend/internal/integrations/payment/leshua/client.go b/backend/internal/integrations/payment/leshua/client.go index 26781eb..7763aa9 100644 --- a/backend/internal/integrations/payment/leshua/client.go +++ b/backend/internal/integrations/payment/leshua/client.go @@ -77,6 +77,7 @@ type VerifyNotifyResult struct { MatchedKey string Got string Expected map[string]string + BaseString map[string]string ParamKeys []string } @@ -185,19 +186,19 @@ func (c *Client) VerifyNotify(params map[string]string) bool { func (c *Client) VerifyNotifyDetail(params map[string]string) VerifyNotifyResult { got := strings.ToUpper(params["sign"]) result := VerifyNotifyResult{ - Got: got, - Expected: map[string]string{}, - ParamKeys: notifyParamKeys(params), + Got: got, + Expected: map[string]string{}, + BaseString: map[string]string{}, + ParamKeys: notifyParamKeys(params), } if got == "" { return result } for _, item := range c.notifyKeyCandidates() { - expected := Sign(params, item.key, SignOptions{ - IncludeEmpty: true, - ExcludeKeys: []string{"error_code", "sign"}, - }) + expected := Sign(params, item.key, notifySignOptions()) + baseString := SignBaseString(params, notifySignOptions()) result.Expected[item.name] = expected + result.BaseString[item.name] = baseString if got == expected { result.OK = true result.MatchedKey = item.name @@ -207,6 +208,13 @@ func (c *Client) VerifyNotifyDetail(params map[string]string) VerifyNotifyResult return result } +func notifySignOptions() SignOptions { + return SignOptions{ + IncludeEmpty: true, + ExcludeKeys: []string{"error_code", "leshua", "sign"}, + } +} + type notifyKeyCandidate struct { name string key string @@ -217,16 +225,13 @@ func (c *Client) notifyKeyCandidates() []notifyKeyCandidate { if c.cfg.NotifyKey != "" { candidates = append(candidates, notifyKeyCandidate{name: "notify_key", key: c.cfg.NotifyKey}) } - if c.cfg.SignKey != "" && c.cfg.SignKey != c.cfg.NotifyKey { - candidates = append(candidates, notifyKeyCandidate{name: "sign_key", key: c.cfg.SignKey}) - } return candidates } func notifyParamKeys(params map[string]string) []string { keys := make([]string, 0, len(params)) for key := range params { - if key == "sign" || key == "error_code" { + if key == "sign" || key == "error_code" || key == "leshua" { continue } keys = append(keys, key) @@ -276,6 +281,16 @@ type SignOptions struct { } func Sign(params map[string]string, key string, opts SignOptions) string { + baseString := SignBaseString(params, opts) + stringSignTemp := "key=" + key + if baseString != "" { + stringSignTemp = baseString + "&key=" + key + } + sum := md5.Sum([]byte(stringSignTemp)) + return strings.ToUpper(hex.EncodeToString(sum[:])) +} + +func SignBaseString(params map[string]string, opts SignOptions) string { excluded := map[string]bool{} for _, item := range opts.ExcludeKeys { excluded[item] = true @@ -298,9 +313,7 @@ func Sign(params map[string]string, key string, opts SignOptions) string { for _, name := range keys { parts = append(parts, name+"="+params[name]) } - parts = append(parts, "key="+key) - sum := md5.Sum([]byte(strings.Join(parts, "&"))) - return strings.ToUpper(hex.EncodeToString(sum[:])) + return strings.Join(parts, "&") } func ParsePayload(body []byte) (map[string]string, error) { @@ -342,6 +355,7 @@ func parseXMLPayload(body []byte) (map[string]string, error) { decoder := xml.NewDecoder(bytes.NewReader(body)) out := map[string]string{} var current string + depth := 0 for { token, err := decoder.Token() if err == io.EOF { @@ -352,14 +366,25 @@ func parseXMLPayload(body []byte) (map[string]string, error) { } switch item := token.(type) { case xml.StartElement: - current = item.Name.Local + depth++ + if depth > 1 { + current = item.Name.Local + if _, exists := out[current]; !exists { + out[current] = "" + } + } case xml.CharData: value := strings.TrimSpace(string(item)) - if current != "" && current != "xml" && value != "" { + if current != "" && value != "" { out[current] = value } case xml.EndElement: - current = "" + if current == item.Name.Local { + current = "" + } + if depth > 0 { + depth-- + } } } return out, nil diff --git a/backend/internal/integrations/payment/leshua/client_test.go b/backend/internal/integrations/payment/leshua/client_test.go index 22b0f1d..72f9a87 100644 --- a/backend/internal/integrations/payment/leshua/client_test.go +++ b/backend/internal/integrations/payment/leshua/client_test.go @@ -1,6 +1,7 @@ package leshua import ( + "strings" "testing" "hfb_sys/backend/internal/config" @@ -22,6 +23,12 @@ func TestSignUsesASCIISortedNonEmptyParams(t *testing.T) { if got != want { t.Fatalf("Sign() = %s, want %s", got, want) } + + baseString := SignBaseString(params, SignOptions{}) + wantBaseString := "amount=100&merchant_id=1234567890&nonce_str=abc&service=get_tdcode&third_order_id=NO1" + if baseString != wantBaseString { + t.Fatalf("SignBaseString() = %s, want %s", baseString, wantBaseString) + } } func TestVerifyNotifyIncludesEmptyAndExcludesErrorCode(t *testing.T) { @@ -33,11 +40,12 @@ func TestVerifyNotifyIncludesEmptyAndExcludesErrorCode(t *testing.T) { "amount": "100", "status": "2", "attach": "", + "leshua": "", "error_code": "-20001", } params["sign"] = Sign(params, "notify-secret", SignOptions{ IncludeEmpty: true, - ExcludeKeys: []string{"error_code", "sign"}, + ExcludeKeys: []string{"error_code", "leshua", "sign"}, }) if !client.VerifyNotify(params) { @@ -50,7 +58,7 @@ func TestVerifyNotifyIncludesEmptyAndExcludesErrorCode(t *testing.T) { } } -func TestVerifyNotifyFallsBackToSignKey(t *testing.T) { +func TestVerifyNotifyDoesNotFallBackToSignKey(t *testing.T) { client := NewClient(config.LeshuaPaymentConfig{ NotifyKey: "wrong-notify-secret", SignKey: "sign-secret", @@ -67,12 +75,64 @@ func TestVerifyNotifyFallsBackToSignKey(t *testing.T) { ExcludeKeys: []string{"error_code", "sign"}, }) + result := client.VerifyNotifyDetail(params) + if result.OK { + t.Fatal("VerifyNotifyDetail().OK = true, want false") + } + if _, ok := result.Expected["sign_key"]; ok { + t.Fatal("VerifyNotifyDetail() unexpectedly used sign_key fallback") + } +} + +func TestVerifyNotifyUsesDocumentedNotifySignature(t *testing.T) { + client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"}) + params := map[string]string{ + "merchant_id": "1234567890", + "third_order_id": "NO1", + "leshua_order_id": "LS1", + "amount": "100", + "status": "2", + "sign_type": "MD5", + } + params["sign"] = Sign(params, "notify-secret", SignOptions{ + IncludeEmpty: true, + ExcludeKeys: []string{"error_code", "leshua", "sign"}, + }) + result := client.VerifyNotifyDetail(params) if !result.OK { t.Fatal("VerifyNotifyDetail().OK = false, want true") } - if result.MatchedKey != "sign_key" { - t.Fatalf("MatchedKey = %s, want sign_key", result.MatchedKey) + if result.MatchedKey != "notify_key" { + t.Fatalf("MatchedKey = %s, want notify_key", result.MatchedKey) + } +} + +func TestVerifyNotifyKeepsEmptyXMLFieldsInSignature(t *testing.T) { + client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"}) + params, err := ParsePayload([]byte(` +100 + +1234567890 +MD5 +2 +NO1 +`)) + if err != nil { + t.Fatalf("ParsePayload(xml) error = %v", err) + } + params["sign"] = Sign(params, "notify-secret", SignOptions{ + IncludeEmpty: true, + ExcludeKeys: []string{"error_code", "leshua", "sign"}, + }) + + result := client.VerifyNotifyDetail(params) + if !result.OK { + t.Fatal("VerifyNotifyDetail().OK = false, want true") + } + baseString := result.BaseString[result.MatchedKey] + if !strings.Contains(baseString, "goods_tag=") { + t.Fatalf("baseString = %s, want goods_tag included", baseString) } } @@ -85,11 +145,17 @@ func TestParsePayloadSupportsFormAndXML(t *testing.T) { t.Fatalf("ParsePayload(form) = %#v", form) } - xml, err := ParsePayload([]byte("NO26")) + xml, err := ParsePayload([]byte("NO26")) if err != nil { t.Fatalf("ParsePayload(xml) error = %v", err) } if xml["third_order_id"] != "NO2" || xml["status"] != "6" { t.Fatalf("ParsePayload(xml) = %#v", xml) } + if value, ok := xml["goods_tag"]; !ok || value != "" { + t.Fatalf("ParsePayload(xml).goods_tag = %q, exists=%v; want empty value", value, ok) + } + if value, ok := xml["coupon"]; !ok || value != "" { + t.Fatalf("ParsePayload(xml).coupon = %q, exists=%v; want empty value", value, ok) + } } diff --git a/backend/internal/modules/payment/handler.go b/backend/internal/modules/payment/handler.go index a91d0ac..2ea0be0 100644 --- a/backend/internal/modules/payment/handler.go +++ b/backend/internal/modules/payment/handler.go @@ -110,8 +110,18 @@ func (h *Handler) LeshuaNotify(c *gin.Context) { c.String(http.StatusBadRequest, "FAIL") return } - log.Printf("[payment] leshua notify received third_order_id=%s leshua_order_id=%s status=%s amount=%s", params["third_order_id"], params["leshua_order_id"], params["status"], params["amount"]) - result, err := h.service.HandleLeshuaNotify(params) + rawPayload := string(body) + contentType := c.GetHeader("Content-Type") + log.Printf( + "[payment] leshua notify received third_order_id=%s leshua_order_id=%s status=%s amount=%s content_type=%s raw_payload=%s", + params["third_order_id"], + params["leshua_order_id"], + params["status"], + params["amount"], + contentType, + rawPayload, + ) + result, err := h.service.HandleLeshuaNotify(params, rawPayload, contentType) if err != nil || result == nil || !result.OK { log.Printf("[payment] leshua notify failed third_order_id=%s err=%v", params["third_order_id"], err) c.String(http.StatusOK, "FAIL") diff --git a/backend/internal/modules/payment/repository.go b/backend/internal/modules/payment/repository.go index 2bc12ae..078500a 100644 --- a/backend/internal/modules/payment/repository.go +++ b/backend/internal/modules/payment/repository.go @@ -125,7 +125,7 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymentRequest, clientIP string) (*PaymentDTO, error) { amountCent := moneyCent(req.Amount) - if userID == 0 || amountCent <= 0 { + if userID == 0 || req.Amount < MinWalletRechargeAmount || amountCent <= 0 { return nil, ErrPaymentCannotStart } payment, err := r.createWalletRechargePayment(userID, amountCent, req) @@ -241,17 +241,22 @@ func (r *Repository) Query(userID uint64, orderID uint64) (*PaymentDTO, error) { return &dto, nil } -func (r *Repository) HandleLeshuaNotify(params map[string]string) (*NotifyResult, error) { +func (r *Repository) HandleLeshuaNotify(params map[string]string, rawPayload string, contentType string) (*NotifyResult, error) { + var verify leshua.VerifyNotifyResult if !r.isMockMode { - verify := r.leshua.VerifyNotifyDetail(params) + verify = r.leshua.VerifyNotifyDetail(params) if !verify.OK { log.Printf( - "[payment] leshua notify verify failed third_order_id=%s got=%s expected=%v keys=%v", + "[payment] leshua notify verify failed third_order_id=%s got=%s expected=%s keys=%v base_string=%s", params["third_order_id"], - shortSign(verify.Got), - shortExpectedSigns(verify.Expected), + verify.Got, + verify.Expected["notify_key"], verify.ParamKeys, + verify.BaseString["notify_key"], ) + if err := r.recordNotifyDiagnostic(params, rawPayload, contentType, verify, "verify_failed"); err != nil { + log.Printf("[payment] leshua notify diagnostic save failed third_order_id=%s err=%v", params["third_order_id"], err) + } return nil, ErrPaymentVerifyFailed } log.Printf("[payment] leshua notify verified third_order_id=%s matched_key=%s", params["third_order_id"], verify.MatchedKey) @@ -268,9 +273,13 @@ func (r *Repository) HandleLeshuaNotify(params map[string]string) (*NotifyResult return nil, err } if amount := parseCent(params["amount"]); amount > 0 && amount != payment.AmountCent { + if err := r.recordNotifyDiagnostic(params, rawPayload, contentType, verify, "amount_mismatch"); err != nil { + log.Printf("[payment] leshua notify diagnostic save failed third_order_id=%s err=%v", params["third_order_id"], err) + } return nil, ErrPaymentVerifyFailed } - if err := r.applyChannelStatus(&payment, params["status"], params["pay_time"], params, channelSourceNotify); err != nil { + raw := withNotifyDiagnostic(params, rawPayload, contentType, verify, "verified") + if err := r.applyChannelStatus(&payment, params["status"], params["pay_time"], raw, channelSourceNotify); err != nil { return nil, err } return &NotifyResult{OK: true, Message: "000000"}, nil @@ -531,6 +540,40 @@ func withRawSource(raw map[string]string, source string) map[string]string { return out } +func (r *Repository) recordNotifyDiagnostic(params map[string]string, rawPayload string, contentType string, verify leshua.VerifyNotifyResult, status string) error { + thirdOrderID := params["third_order_id"] + if thirdOrderID == "" { + return nil + } + raw := withNotifyDiagnostic(params, rawPayload, contentType, verify, status) + return r.db.Model(&model.PaymentOrder{}). + Where("third_order_id = ?", thirdOrderID). + Update("raw_response", jsonMap(raw)).Error +} + +func withNotifyDiagnostic(params map[string]string, rawPayload string, contentType string, verify leshua.VerifyNotifyResult, status string) map[string]string { + raw := withRawSource(params, channelSourceNotify) + raw["_notify_diagnostic_status"] = status + raw["_raw_payload"] = rawPayload + raw["_raw_content_type"] = contentType + raw["_sign_got"] = verify.Got + raw["_sign_matched_key"] = verify.MatchedKey + raw["_sign_expected"] = jsonString(verify.Expected) + raw["_sign_base_strings"] = jsonString(verify.BaseString) + return raw +} + +func jsonString(value map[string]string) string { + if len(value) == 0 { + return "{}" + } + raw, err := json.Marshal(value) + if err != nil { + return "{}" + } + return string(raw) +} + func newPaymentNo() (string, error) { buf := make([]byte, 4) if _, err := rand.Read(buf); err != nil { @@ -547,21 +590,3 @@ func firstNonEmpty(values ...string) string { } return "" } - -func shortExpectedSigns(values map[string]string) map[string]string { - out := map[string]string{} - for key, value := range values { - out[key] = shortSign(value) - } - return out -} - -func shortSign(value string) string { - if value == "" { - return "" - } - if len(value) <= 12 { - return value - } - return value[:12] + "..." -} diff --git a/backend/internal/modules/payment/service.go b/backend/internal/modules/payment/service.go index 472fe53..5cf3921 100644 --- a/backend/internal/modules/payment/service.go +++ b/backend/internal/modules/payment/service.go @@ -10,6 +10,8 @@ var ( ErrPaymentNotFound = errors.New("payment not found") ) +const MinWalletRechargeAmount = 0.01 + type Service struct { repo *Repository } @@ -42,7 +44,7 @@ func (s *Service) StartWalletRecharge(userID uint64, req WalletRechargePaymentRe if s.repo == nil { return nil, ErrDependencyUnavailable } - if userID == 0 || req.Amount <= 0 { + if userID == 0 || req.Amount < MinWalletRechargeAmount { return nil, ErrPaymentCannotStart } return s.repo.StartWalletRecharge(userID, req, clientIP) @@ -58,9 +60,9 @@ func (s *Service) QueryWalletRecharge(userID uint64, paymentID uint64) (*Payment return s.repo.QueryWalletRecharge(userID, paymentID) } -func (s *Service) HandleLeshuaNotify(params map[string]string) (*NotifyResult, error) { +func (s *Service) HandleLeshuaNotify(params map[string]string, rawPayload string, contentType string) (*NotifyResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.HandleLeshuaNotify(params) + return s.repo.HandleLeshuaNotify(params, rawPayload, contentType) } diff --git a/backend/internal/modules/wallet/service.go b/backend/internal/modules/wallet/service.go index d7b46c5..0774cbb 100644 --- a/backend/internal/modules/wallet/service.go +++ b/backend/internal/modules/wallet/service.go @@ -8,6 +8,8 @@ var ( ErrInsufficientBalance = errors.New("insufficient balance") ) +const MinRechargeAmount = 0.01 + type Service struct { repo *Repository } @@ -34,7 +36,7 @@ func (s *Service) Recharge(userID uint64, req RechargeRequest) (*AccountDTO, err if s.repo == nil { return nil, ErrDependencyUnavailable } - if req.Amount <= 0 { + if req.Amount < MinRechargeAmount { return nil, ErrInvalidAmount } return s.repo.Recharge(userID, req.Amount) diff --git a/docs/乐刷商户交易.md b/docs/乐刷商户交易.md new file mode 100644 index 0000000..abe7e25 --- /dev/null +++ b/docs/乐刷商户交易.md @@ -0,0 +1,1699 @@ +## +### API目录 +| **分类** | **接口中文名** | **接口英文名** | **描述** | +| --- | --- | --- | --- | +| 下单接口 | [统一下单接口](#YGAT6) | /cgi-bin/lepos_pay_gateway
.cgi | 主扫,C扫B。消费者主动扫码。 | +| 条码支付 | [条码支付](#AGDJI) | /cgi-bin/lepos_pay_gateway.cgi | 被扫,B扫C。消费者出示付款码被扫。 | +| 交易结果 | [支付结果通知](#93D6z) | / | 返回支付结果。**影响推送的因素较多,建议接交易结果查询接口。** | +| | [交易结果查询](#yEuUU) | /cgi-bin/lepos_pay_gateway.cg | 查询交易结果 | +| 退款 | [退款](#Mh3kb) | /cgi-bin/lepos_pay_gateway.cgi | 进行退款操作 | +| | [退款查询](#2D2fx) | /cgi-bin/lepos_pay_gateway.cgi | 查询退款信息 | +| | [退款结果通知](#ZQ0tK) | | 查看退款结果 | +| 订单关闭接口 | [订单关闭](#z9MWc) | /cgi-bin/lepos_pay_gateway.cgi | 关闭订单 | +| 授权码 | [授权码查询用户openid](#F3rn1) | /cgi-bin/lepos_pay_gateway.cgi | 查询openid | +| | [授权码获取银联云闪付用户ID](#DprGi) | | 获取云闪付用户ID | +| 异步通知 | [刷卡、扫码支付异步通知](#ZD2Om) | | | + + +### ****数字签名 +(1)为了保证数据传输过程中的数据真实性和完整性,我们需要对数据进行数字签名,在接收签名数据之后进行签名校验。 + +(2)签名有两个步骤,先按一定规则拼接要签名的原始串,再选择具体的算法和密钥计算出签名结果。 + +(3)原始串中,字段名和字段值都采用原始值,不进行URL Encode。 + +(4)平台返回的应答或通知消息可能会由于升级增加参数,请验证应答签名时注意允许这种情况。 + + + +### 签名方法 +(1)设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 + +(2)在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 + + + +### 签名样例 +(1)假设需要传递的参数如下: + +```plain +server:query_status +merchant_id:1234567890 +third_order_id:12345678901 +nonce_str:12345678901 +``` + + + +(2)对参数按照key=value的格式,并按照参数名ASCII字典序排序如下 + +```plain +stringA=merchant_id=1234567890&nonce_str=12345678901&server=query_status&third_order_id=12345678901 +``` + + + +(3)拼接商户秘钥key + +```plain +stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d" //注:key为商户密钥key +``` + + + +(4)计算签名 + +```plain +交易报文MD5计算:sign=MD5(stringSignTemp).toUpperCase() +通知报文MD5计算:sign=MD5(stringSignTemp).toLowerCase() +交易报文SM3计算:sign=SM3(stringSignTemp).toUpperCase() +通知报文SM3计算:sign=SM3(stringSignTemp).toLowerCase() +``` + +注:md5加密为32位。 +**注**:SM3签名为64位 +(1)设所有请求数据为集合M(请求数据中需上送**sign_type**为**SM3**,不上送默认为使用MD5),将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序), + +使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 + +(2)在stringA最后拼接上key得到stringSignTemp字符串, + +并对stringSignTemp进行SM3运算,再将得到的字符串所有字符转换为大写,得到sign值signValue + +### 特别注意 +(1)请求数据验签方法 + +```plain +除sign、leshua、resp_code字段外,其他参数按照签名方法验签,空值不传递,不参与签名组串。 +``` + + + +(2)应答数据验签方法 + +```plain +除sign、leshua、resp_code字段外,其他参数按照签名方法验签,空值参与签名组串。 +(注:微信刷脸接口是全字段加签) +``` + + + +(3)支付结果通知验签方法 + +```plain +除sign、leshua、error_code字段外,其他参数按照签名方法验签,空值参与签名组串。 +``` + +商户维度接入,支付结果通知验签需要向商务申请 商户获取代理商解析密钥 + +### 判断付款码规则 + +```java +支付宝:支付授权码,25~30开头的长度为16~24位的数字, 实际字符串长度以开发者获取的付款码长度为准 +微信:扫码支付授权码,设备读取用户微信中的条码或二维码信息(注:用户刷卡条形码规则:18位纯数字以10、11、12、13、14、15开头) +银联二维码:62开头19位数字 +数字人民币:0100开头的数字,其中01002 工行(数字人民币的不完整,后续会有改动) +01003 农行 +01004 中行 +01005 建行 +01007 网商 +01008 邮储 +01009 交通银行 +``` + +### 1、统一下单(主扫:C扫B) +#### 1.1接口说明 +代理商在C扫B场景调用此支付接口完成下单,支持支付宝拉码支付、公众号支付、服务窗支付、银联JS支付、微信小程序支付。 + +测试商户信息及请求密钥和回调密钥请联系我们获取。 + +生产环境走完协议流程并且联调完提交测试报告后,我司运营同事将会提供参数给贵司。 + + + +#### 1.2请求方式 +POST + + + +#### 1.3请求地址 +测试环境域名:https://t-paygate.lepass.cn + +生产环境域名:https://paygate.leshuazf.com + +接口地址:/cgi-bin/lepos_pay_gateway.cgi + + + +#### 1.4频率限制 +4000次/s + + + +#### 1.5业务入参 + + +| **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | +| service | M | 无 | string | **接口名**
get_tdcode(此为固定值) | +| t0 | O | 无 | int | **T0交易标志**
默认为0,0:d1交易 1:d0交易 | +| sign_type | 0 | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使用MD5算法) | +| pay_way | M | 16 | string | **支付类型**
详见下方[支付类型](#nlHUH) | +| merchant_id | M | 10 | string | **乐刷商户号**
由乐刷分配 | +| user_name | O | 无 | string | 收银员名称 | +| third_order_id | M | 64 | string | **商户内部订单号**
可以包含字母:确保同一个商户下唯一 | +| amount | M | 10 | string | **订单总金额**
金额不能为零或负数 | +| jspay_flag | M | 1 | **string** | **支付类型**
0-支付宝Native扫码支付、银联Native扫码支付;
1-微信JSAPI、支付宝JSAPI支付、银联JSAPI支付;
2-微信、支付宝简易支付<跳转乐刷收银台支付>;
(jspay_flag=2时必传jump_url,否则会报错)
3-微信小程序支付、支付宝小程序支付
注:
1)微信拉码支付已下线;
2)如需接入银联JS支付,请联系乐刷运营沟通域名报备。
3)数字货币支付当前仅支持jspay_flag=0 | +| royalty | O | 1 | string | 交易分账标识
0:普通交易 1:分账(用于分账的交易需传该参数) 2:交易主体分账(延迟结算)
3:预授权
注:如果未传该字段默认为0,按不分账处理 | +| appid | M | 32 | string | **appid**
条件必填,微信支付时必填。填微信公众号支付的公众号id或微信小程序支付的小程序appid;
传了会使用此appid 进行下单 | +| sub_openid | [C](#eFgvt) | 32 | string | **sub_openid**
微信JSAPI、微信小程序、支付宝JSAPI、支付宝小程序、银联JSAPI支付必填 | +| extend_business_params | O | 无 | string | 该字段 为JSON格式的字符串,当前可透传支付宝的参数见下表
具体格式详见文档下方:[业务拓展参数示例](#5u5k5)注:花呗分期商家订单传参贴息示例:
"extend_business_params":"{\"business_params\":{ \"enable_thirdparty_subsidy\":\"N\"}}"
如果第三⽅贴息活动为全场贴息,默认可贴息,传⼊ N 值则本笔交易禁⽤三⽅贴息;
如果第三⽅贴息活动为指定订单传参贴息,默认禁⽤贴息,传⼊Y 值则本笔交易启⽤三⽅贴息 | +| jump_url | O | 128 | string | **前台跳转地址**
简易支付时必填,完成后,乐刷将跳转到该页面,需做UrlEncode 处理
(受微信支付宝限制,已失效,可不传) | +| notify_url | O | 128 | string | **通知地址**
接收乐刷通知(支付结果通知)的URL,需做UrlEncode 处理,需要绝对路径,确保乐刷能正确访问,若不需要回调请忽略 | +| client_ip | O | 16 | string | **商户发起交易的IP地址** | +| body | O | 256 | string | **商品描述**
不能包含回车换行等特殊字符 | +| shop_no | O | 16 | string | **商户门店编号**
只能是汉字、英文字母、数字。注:该参数不上传给微信、支付宝 | +| pos_no | O | 16 | string | **商户终端编号**
只能是汉字、英文字母、数字 注意:银联类型交易时,只能 8位:数字字母 不支持 特殊字符 | +| attach | O | 96 | string | **附加数据**
下单成功原样返回;注意:只能是汉字、英文字母、数字 | +| limit_pay | O | 1 | string | **指定支付方式**
1:禁止使用信用卡;
0或者不填:不限制 | +| goods_tag | O | 32 | string | **订单优惠标记**
订单优惠标记(订单级别-品类级别),透传给微信,对应微信的`goods_tag`字段。微信会自动进行优惠匹配计算和核销。
用于给整个订单打上一个"标签",主要用于**代金券立减优惠场景**,示例:`"coffee"`。 | +| goods_detail | O | 8000 | string | **单品详情**
**微信**
单品级详细信息,透传给微信,对应微信的`detail`字段,**注意**不是对应`detail.goods_detail`字段。微信会自动进行优惠匹配计算和核销。
按微信单品优惠券格式传递透传给微信,请做UrlEncode
示例:`{"goods_detail":[{"merchant_goods_id":"xiaomi_14_pro","goods_name":"小米14 Pro","quantity":1,"unit_price":499900}]}`

**银联**
银联扫码交易时,透传给银联的“收款方附加数据”acqAddnData参数,当前银联的单品营销及扫码点餐均使用到该参数,具体参数格式及说明见:[银联收款方附加数据](https://www.yuque.com/hayley-boppa/ws2xbg/tu6kh5hh2kegkz0c#lUVoy)。 | +| order_expiration | O | 无 | string | **订单有效时间**
单位:秒(支付宝的超时时间最小粒度为分钟,建议上送的为60的整数倍)最大时效600s | +| hb_fq_num | O | 无 | string | **花呗分期数**
支付宝花呗分期数,支持3、6、12期 | +| front_url | C | 无 | string | **前端跳转地址**
银联JSAPI支付时选填,支付成功时跳转 | +| front_fail_url | C | 无 | string | **支付失败前端跳转地址**
银联JSAPI支付时选填,支付失败时跳转 | +| nonce_str | M | 32 | string | **随机字符串** | +| sign | M | 32 | string | **签名**
MD5签名结果 | +| device_info | O | 32 | string | **设备编号**
针对微信、支付宝有效,透传至银联。
微信:数字、字母、下划线格式
支付宝:目前支持非0开头的数字 | +| store_id | O | 32 | string | **门店编号**
针对微信、支付宝有效,透传至银联。
微信:数字、字母、下划线格式 | +| ass_merchant_id | O | 32 | string | **通道商户号**
非必填。可使用指定的微信、支付宝子商户号进行交易,一个乐刷商户号有多个通道商户号情况下可用。
注:对应乐刷商户号大于200条时不能传该参数 | +| pnrins_id_cd | O | 32 | string | **银联服务商标识**
银联扫码交易时,透传给银联,对应银联的 pnrInsIdCd参数。 | +| gps_info | O | 512 | string | **gps信息**
校验GPS信息格式, 如:-128.12,23.1 -- 1. xxx,xxx -- 2. 经度xxx为[-180,180]之间的浮点数,维度xxx为[-90,90]之间的浮点数,最多保留小数点八位float(11,8) | +| bs_info | O | 512 | string | **基站信息**
校验基站信息格式,
格式说明: -- 1. 格式为:xxx,xxx,xxx,xxx 如:460,1,12,23454 -- 2. xxx均为整数,长度不超过11位

基站信息,从左至右依次对应以下几个字段: mcc: 移动国家号码,由3位数字组成,唯一地识别移动用户所属的国家。我国为460 mnc:移动网号,由两位数字组成,用于识别移动用户所归属的移动网 lac:位置区编码,为寻呼而设置的一个区域,覆盖一片地理区域,按寻呼量划分 ci :负责收发的天线所朝向的扇区编号 | +| extend_business_params | O | - | string | 见下extend_business_params参数表 | +| scene_info | O | - | string | 该字段用于上报场景信息,仅针对微信交易有效,目前支持上报实际门店信息。该字段 为JSON格式的字符串,对象格式为{"store_info":{"id": "门店 ID","name ": "名称","area_code": "编码","address": "地址"}} ,字段详细说明参考下文。 | +| need_receipt | O | - | boolean | 电子发票功能。仅针对微信交易有效,需要和微信公众平台的发票功能联合使用。
枚举:
(只能为true或false,只支持全小写字母或全大写字母)
1. true
2. false | +| terminal_info | M | - | **String** | 商户侧受理终端信息
**terminal_info为String类型,内容为json**:
terminal_info="{\"longitude\":\"-121.48352\",\"latitude\":\"+31.221345\",\"network_license\":\"P3100\",\"device_type\":\"01\",\"device_id\":\"54MPDz39\",\"serial_num\":\"5\",\"app_version\":\"v1.1.1\",\"device_ip\":\"252.1.1.25\"}"

**terminal_info不能为空**,如无终端信息则按此规则传参:**device_type和serial_num必传:**
**device_type固定传11**
**serial_num=lhsd+商户编号**

json内各参数说明
longitude:经度,+代表东经,-代表西经。
latitude:纬度,+代表北纬,-代表南纬。
network_license:银行卡受理终端入网编号。
编号由“中国银联标识产品企业资质认证办公室”为通过入网认证的终端进行 分配。银联直连终端必填。
格式:5 位字符,例如 P3100
device_type:设备类型,01:自动柜员机(含 ATM 和 CDM)和 多媒体自助终端
02:传统 POS
03mPOS
04:智能 POS
05II 型固定电话
06:云闪付终端;
07:保留使用;
08:手机 POS
09:刷脸付终端;
10:条码支付受理终端;
11:条码支付辅助受理终端;
12:行业终端(公交、地铁用于指定行
业的终端);
13:MIS 终端;

serial_num:设备序列号,设备类型02
03、04、05、06、08、09 或 10 时必填
encrypt_rand_num:仅在被扫支付类交易报文中出现:若付 款码为 19 位数字,则取后 6 位; 若付款码码为 EMV 二维码,则取其 tag 57 的卡号/token 号的后 6 位
secret_text:仅在条码支付交易报文中出现:64bit
的密文数据,对终端硬件序列号和加密 随机因子加密后的结果。 本子域取值为:64bit 密文数据进行 base64 编码后的结果。该字段需要服务商和厂商联调时由厂商sdk之类加密后给出。
app_version:终端应用程序的版本号。应用程序变更
应保证版本号不重复。当长度不足时, 右补空格。固定8位
device_ip:终端交易IP。如经、维度信息未上送,该字段必送。 | + + +**extend_business_params 参数表** + +| **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | :---: | :---: | :---: | --- | +| business_params | O | - | String | | +| goods_detail | O | - | String | | +| extend_params | O | - | String | | +| seller_id | O | 28 | String | 卖家支付宝用户 ID。如果该值为空,则默认为商户签约账号对应的支付宝用户 ID | +| discountable_amont | O | 11 | Price | 参与优惠计算的金额,单位为元,精确到 小 数 点 后 两 位 , 取值范围[0.01,100000000]。 如果该值未传入,但传入了【订单总金额】和【不可打折金额】,则该值默认为【订单总金额】-【不可打折金额】 | +| undiscountable_amount | O | 11 | Price | 不参与优惠计算的金额,单位为元,精确 到 小 数 点 后 两 位 , 取 值 范 围[0.01,100000000]。如果该值未传入,但传入了【订单总金额】和【可打折金额】,则该值默认为【订单总金额】-【可打折金额】 | +| buyer_logon_id | O | 100 | String | 买家支付宝账号 | +| operator_id | O | 28 | String | 商户操作员编号 | +| enable_pay_channels | O | 128 | String | 可用渠道,用户只能在指定渠道范围内支付。当有多个渠道时用“,”分隔,与 disable_pay_channels 互 斥。 | +| settle_info | O | - | SettleInfo | 描述结算信息,json 格式,详见结算参数说明 | +| alipay_store_id | O | 32 | String | 支付宝的店铺编号 | +| ext_user_info | O | - | ExtUserInfo | 外部指定买家 | +| qr_code_timeout_express | O | 6 | String | 该笔订单允许的最晚付款时间,逾期将关闭交易,从生成二维码开始计时。取值范围:1m~15d。 m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在 0 点关闭)。 该参数数值不接受小数点,如 1.5h,可转换为90m。
(对应jspay_flag为0时 | +| merchant_order_no | O | 32 | String | 商户原始订单号,最大长度限制32 位
(对应jspay_flag为1/2/3时 | +| disable_pay_channels | O | 64 | String | 禁用支付渠道,多个渠道以逗号分割,如 同时禁用信用支付类型和积分,则 disable_pay_channels="cre dit_group,point" 具体的渠道以支付宝或者银联提供的为准 | + + +**场景信息scene_info字段说明(当前仅针对微信交易会透传给微信):** + +| **名称** | **参数名** | **必填** | **类型** | **示例值** | **描述** | +| --- | --- | --- | --- | --- | --- | +| 门店Id | id | 否 | String(32) | SZTX001 | 门店唯一标识 | +| 门店名称 | name | 否 | String(64) | 腾讯大厦腾大餐厅 | 门店名称 | +| 门店行政区划码 | area_code | 否 | String(6) | 440305 | 门店所在地行政区划码 | +| 门店详细地址 | address | 否 | String(128) | 科技园中一路腾讯大厦 | 门店详细地址 | + + +#### 1.6业务出参 +| **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | +| resp_code | M | 无 | string | **返回状态码**
0 - 成功,非0 - 失败。注:此字段是通信标识,是否获取到二维码要看result_code | +| resp_msg | C | 无 | string | **返回错误信息**
错误描述:resp_code非0时返回 | +| result_code | M | 1 | string | **业务结果**
0 - 成功,非0 - 失败 | +| error_code | O | 无 | string | **错误码**
[参考错误码信息表](#rOC0m) | +| error_msg | O | 无 | string | **错误信息描述** | +| attach | O | 96 | string | **附加数据**
原样返回;注意:只能是汉字、英文字母、数字 | +| merchant_id | M | 10 | string | **乐刷商户号**
由乐刷分配 | +| sub_merchant_id | M | | string | **通道商户号**
微信、支付宝、QRC商户号 | +| third_order_id | M | 64 | string | **商户内部订单号** | +| nonce_str | M | 32 | string | **随机字符串** | +| sign | M | 32 | string | **MD5签名结果** | +| td_code | C | 无 | string | **二维码链接**
非公众号支付返回,可直接通过该链接生成二维码扫码支付(jspay_flag传0-2该字段才会返回) | +| jspay_info | C | 无 | string | **支付信息**
原生公众号、服务窗、小程序,返回json格式字符串、银联JS支付返回URL,用云闪付打开此链接即可调起支付 生效时间:默认10分钟,具体时间看订单有效时间 | +| jspay_url | C | 无 | string | **支付跳转地址**
简易支付时返回。重定向到该url可以完成后续支付流程。返回二维码链接有效期为10分钟; | +| leshua_order_id | M | 32 | string | **乐刷订单号** | +| pay_way | M | 无 | string | **支付类型**
[详见下方支付类型](#nlHUH) | +| cost_time | C | 无 | string | **请求耗时**
请求接口耗费的时间 | +| channel_flag | O | 32 | string | **通道标记**
GXST:高校食堂
PUBWEL:公益
PUBPAY:公缴
ZFBLH:支付宝新蓝海
SLZXY:私立中小幼
INS:保险
ONLINE:线上
YLLZ:绿洲 | +| alipay_infos | O | | | trade_no:银联交易号 | + + +**注意事项** + +(1)所有涉及到金额的单位都位分,币种为RMB,最小为1分,不允许出现小数 + +(2)文档中参数,标识为必填必须上送(如果缺少会报错),非必填的可填可不填(简易支付"支付跳转地址"字段为必传), + +(3)返回参数中必填为是的参数是一定会返回的,必填为否的参数则不一定返回,因升级或配置等情况实际返回参数可能不会跟文档完全一致,必须以实际接收到的参数为准 + + + +##### 获取微信用户openid +```http +https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842&token=&lang=zh_CN +``` + +##### 获取支付宝用户openid +```http +https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.8ujLD6&treeId=115&articleId=104114&docType=1 +``` + + + + + +**公众号支付请求示例** + +1、请求格式如下: + +```json +https://paygate.leshuazf.com/cgi-bin/lepos_pay_gateway.cgi?amount=1& +appid=wx00e59fa8be9ba128&jspay_flag=1&merchant_id=3219113278& +nonce_str=lnxouwc0r7l93453x5&order_expiration=60&pay_way=WXZF&service=get_tdcode& +sub_openid=oe5BX0qaVuC5vqYdj65GlUcyAVUM&third_order_id=202004271809453917&sign=2A500B7F906284717212304A9BCFB33D +``` + + + +**返回示例** + +1、响应格式如下: + +```json + + + + + + + + + + + + +``` + + + + + +**支付宝拉码支付请求示例** + +1、请求格式如下: + +```json +https://t-paygate.lepass.cn/cgi-bin/lepos_pay_gateway.cgi?amount=1&jspay_flag=0 +&merchant_id=0000000018&nonce_str=w1hxpdiu9nkjfw5qrp&order_expiration=60& +pay_way=ZFBZF&service=get_tdcode&third_order_id=202004291733230374& +sign=340DB607E3BEE2BAE60807B3601008E2 +``` + + + +**返回示例** + +1、响应格式如下: + +```json + + + + + + + + + + + + +``` + + + +### 2、条码支付(被扫:B扫C) + + +**请求地址** + +(1)/cgi-bin/lepos_pay_gateway.cgi + +(2)测试商户信息及请求密钥和回调密钥请联系我们获取。 + +(3)生产环境走完协议流程并且联调完提交测试报告后,我司运营同事将会提供参数给贵司 + + + +**请求参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 接口名 | service | [M](#eFgvt) | 无 | string | upload_authcode(此为固定值) | +| t0交易标志 | t0 | O | 无 | int | 默认为0,0:d1交易 1:d0交易 | +| sign_type | sign_type | O | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使用MD5算法) | +| 付款码 | auth_code | M | 无 | string | 支持以下付款码:
1. 微信数字付款码
2. 支付宝数字付款码
3. 云闪付APP 62开头付款码
4. 银行APP 62开头付款码
5. 数字人民币APP付款码(0100开头)
6. 支付宝刷脸付款码(fp开头) | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号,可以包含字母:确保同一个商户下唯一 | +| 订单金额 | amount | M | 10 | string | 总金额 | +| 分账标识 | royalty | O | 1 | string | 交易分账标识
0:普通交易 1:分账(用于分账的交易需传该参数) 2:交易主体分账(延迟结算)
3:预授权
注:如果未传该字段默认为0,按不分账处理 | +| 商户公
众账号 ID | appid | O | 32 | string | 商户或者商户所属渠道号主体的公众号appid,由微信统一分配,仅对微信交易生效,传送前请确定商户和该appid已经建立绑定关系 | +| 通知地址 | notify_url | [O](#eFgvt) | 128 | string | 接收乐刷通知的URL,需做UrlEncode 处理,需要绝对路径,确保乐刷能正确访问,若不需要回调请忽略 | +| 用户IP地址 | client_ip | O | 16 | string | 商户发起交易的IP地址 | +| 商品描述 | body | O | 256 | string | 商品描述,不能包含回车换行等特殊字符 | +| 商户门店编号 | shop_no | O | 16 | string | 只能是汉字、英文字母、数字 | +| 商户终端编号 | pos_no | O | 16 | string | 只能是汉字、英文字母、数字 注意:银联类型交易时,只能 8位:数字字母 不支持 特殊字符 | +| 附加数据 | attach | O | 96 | string | 支付成功原样返回;注意:只能是汉字、英文字母、数字 | +| 指定支付方式 | limit_pay | O | 1 | string | 1:禁止使用信用卡;0或者不填:不限制 | +| 订单优惠标记 | goods_tag | O | 32 | string | 订单优惠标记,透传给微信 | +| 商品详情 | goods_detail | O | 8000 | string | 按[微信单品优惠券格式](https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_101&index=1)传递,透传给微信;
银联扫码交易时,透传给银联的“收款方附加数据”acqAddnData参数,当前银联的单品营销及扫码点餐均使用到该参数,具体参数格式及说明见:[银联收款方附加数据](#lUVoy)。 | +| 花呗分期数 | hb_fq_num | O | 无 | string | 支付宝花呗分期数,支持3、6、12期 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 业务拓展参数 | extend_business_params | O | 8000 | string | 该字段 为JSON格式的字符串,当前可透传支付宝的参数见下表
具体格式详见文档下方:[业务拓展参数示例](#5u5k5)
注:花呗分期商家订单传参贴息示例:
"extend_business_params":"{\"business_params\":{ \"enable_thirdparty_subsidy\":\"N\"}}"
如果第三⽅贴息活动为全场贴息,默认可贴息,传⼊ N 值则本笔交易禁⽤三⽅贴息;
如果第三⽅贴息活动为指定订单传参贴息,默认禁⽤贴息,传⼊Y 值则本笔交易启⽤三⽅贴息 | +| 设备编号 | device_info | O | 32 | string | 针对微信、支付宝有效,透传至银联。
微信:数字、字母、下划线格式
支付宝:目前支持非0开头的数字 | +| 门店编号 | store_id | O | 32 | string | 针对微信、支付宝有效,透传至银联。
微信:数字、字母、下划线格式
支付宝:目前支持非0开头的数字 | +| 通道商户号 | ass_merchant_id | O | 32 | string | 非必填。可使用指定的微信、支付宝子商户号进行交易,一个乐刷商户号有多个通道商户号情况下可用。
注:对应乐刷商户号大于200条时不能传该参数 | +| 银联服务商标识 | pnrins_id_cd | O | 32 | string | 银联扫码交易时,透传给银联,对应银联的 pnrInsIdCd参数。 | +| **gps信息** | gps_info | O | 512 | string | 校验GPS信息格式, 如:-128.12,23.1 -- 1. xxx,xxx -- 2. 经度xxx为[-180,180]之间的浮点数,维度xxx为[-90,90]之间的浮点数,最多保留小数点八位float(11,8) | +| ** 基站信息** | bs_info | O | 512 | string | 校验基站信息格式,
格式说明: -- 1. 格式为:xxx,xxx,xxx,xxx 如:460,1,12,23454 -- 2. xxx均为整数,长度不超过11位

基站信息,从左至右依次对应以下几个字段: mcc: 移动国家号码,由3位数字组成,唯一地识别移动用户所属的国家。我国为460 mnc:移动网号,由两位数字组成,用于识别移动用户所归属的移动网 lac:位置区编码,为寻呼而设置的一个区域,覆盖一片地理区域,按寻呼量划分 ci :负责收发的天线所朝向的扇区编号 | +| 场景信息 | scene_info | O | | string | 该字段用于上报场景信息,仅针对微信交易有效,目前支持上报实际门店信息。该字段 为JSON格式的字符串,对象格式为{"store_info":{"id": "门店 ID","name ": "名称","area_code": "编码","address": "地址"}} ,字段详细说明参考下文。 | +| 电子发票功能 | need_receipt | O | | boolean | 电子发票功能。仅针对微信交易有效,需要和微信公众平台的发票功能联合使用。
枚举:
(只能为true或false,只支持全小写字母或全大写字母)
1. true
2. false | +| 交易终端信息 | terminal_info | O | | String | 商户侧受理终端信息,格式为json 格 式。 terminal_info= { "longitude":"-121.48352",#option "latitude":"+31.221345",#option
"network_license":"P3100", "device_type":"01", "device_id":"54MPDz39", "serial_num":"5", "app_version": "v1.1.1" , "device_ip": "252.1.1.25" , }

json内各参数说明
longitude:经度,+代表东经,-代表西经。
latitude:纬度,+代表北纬,-代表南纬。
network_license:银行卡受理终端入网编号。
编号由“中国银联标识产品企业资质认证办公室”为通过入网认证的终端进行 分配。银联直连终端必填。
格式:5 位字符,例如 P3100
device_type:设备类型,01:自动柜员机(含 ATM 和 CDM)和 多媒体自助终端
02:传统 POS
03mPOS
04:智能 POS
05II 型固定电话
06:云闪付终端;
07:保留使用;
08:手机 POS
09:刷脸付终端;
10:条码支付受理终端;
11:条码支付辅助受理终端;
12:行业终端(公交、地铁用于指定行
业的终端);
13:MIS 终端;

serial_num:设备序列号,设备类型02
03、04、05、06、08、09 或 10 时必填
encrypt_rand_num:仅在被扫支付类交易报文中出现:若付 款码为 19 位数字,则取后 6 位; 若付款码码为 EMV 二维码,则取其 tag 57 的卡号/token 号的后 6 位
secret_text:仅在条码支付交易报文中出现:64bit
的密文数据,对终端硬件序列号和加密 随机因子加密后的结果。 本子域取值为:64bit 密文数据进行 base64 编码后的结果。该字段需要服务商和厂商联调时由厂商sdk之类加密后给出。
app_version:终端应用程序的版本号。应用程序变更
应保证版本号不重复。当长度不足时, 右补空格。固定8位
device_ip:终端交易IP。如经、维度信息未上送,该字段必送。

| + + +extend_business_params参数: + +| **变量名** | **必填** | **限制** | **类型** | **说明** | 示例 | +| --- | --- | --- | --- | --- | --- | +| business_params | O | - | Object | 商户传入业务信息,应用于安全,营销
等参数直传场景,格式为 json 格式。 | | +| goods_detail | O | - | Array | 订单包含的商品列表信息,数组格式。
一般在支付宝扫码点餐业务中上传点餐的商品信息 | [{"goods_id": "5544", "goods_name": "爽口青笋丝","price": "0.50","quantity": 2,"goods_category": "888", "categories_tree": "888","body": "爽口青笋丝","show_url":"pageFoodChain/storeDetail/main?suid=10"}] | +| ┖goods_id | M | 32 | String | 商品的编号 | | +| ┖alipay_goods_id | O | 32 | String | 支付宝定义的统一商品编号 | | +| ┖goods_name | M | 256 | String | 商品名称 | | +| ┖quantity | M | 10 | Number | 商品数量 | | +| ┖price | M | 9 | Price | 商品价格,单位为元 | | +| ┖goods_category | O | 24 | String | 商品类目 | | +| ┖categories_tree | O | 128 | String | 商品类目数,从商户品类目根节点到叶子节点的类目id组成,类目id使用|分割 | 124868003|126
232002|126252
004 | +| ┖body | O | 1000 | String | 商品描述信息 | | +| ┖show_url | O | 400 | String | 商品的展示地址 | http://www.alipay.co
m/xxx.jpg | +| extend_params | O | - | Object | 支付宝业务扩展参数 | | +| ┖sys_service_provid
er_id | O | | String | | | +| ┖hb_fq_num | O | | String | | | +| ┖hb_fq_seller_percent | O | | String | | | +| ┖industry_reflux_info | O | | String | | | +| ┖card_type | O | | String | | | +| | | | | | | +| | | | | | | +| ┖ | | | | | | +| ┖ | | | | | | +| ┖ | | | | | | +| ┖ | | | | | | +| seller_id | O | 28 | String | 如果该值为空,则默认为商户签约账号对应的支付宝用户ID预授权场景下无需上送 | | +| discountable_amont | O | 11 | Price | 参与优惠计算的金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]。 如果该值未传入,但传入了【订单总金额】和【不可打折金额】,则该值默认为【订单总金额】-【不可打折金额】 | | +| undiscountable_amount | O | 11 | Price | 不参与优惠计算的金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]。如果该值未传入,但传入了【订单总金额】和【可打折金额】,则该值默认为【订单总金额】-【可打折金额】 | | +| operator_id | O | 28 | String | 商户操作员编号 | | +| alipay_store_id | O | 32 | String | 支付宝的店铺编号 | | +| merchant_order_no | O | 32 | String | 商户的原始订单号 | | +| ext_user_info | O | - | ExtUserInfo | 外部指定买家 | | +| promo_params | O | 512 | PromoParam | 优惠明细参数,通过此属性补充营销参数 | | +| terminal_params | O | 2048 | String | json 格式;商户传入终端设备。如:
{"credential":"28763443825664394:20180207192030954:abcdefGHIJKLMN","signature":"xxxxxxx","terminalType":"IOT"} | | +| advance_payment_type | O | 20 | String | 支付模式类型, 若 值 为ENJOY_PAY_V2表示当前交易允许走先享后付 2.0 垫资 | | +| query_options | O | 1024 | String[] | 返回查询选项,商户通过上送该参数来定制同步需要额外返回的信息字段,数组格式。如:["fund_bill_list","voucher_detail_list","discount_goods_detail"] | | +| disable_pay_channels | O | 64 | String | 禁用支付渠道,多个渠道以逗号分割,如 同时禁用信用支付类型和积分,则 disable_pay_channels="cre dit_group,point" 具体的渠道以支付宝或者银联提供的为准 | | + + +**场景信息scene_info字段说明(当前仅针对微信交易会透传给微信):** + +| **名称** | **参数名** | **必填** | **类型** | **示例值** | **描述** | +| --- | --- | --- | --- | --- | --- | +| 门店Id | id | 否 | String(32) | SZTX001 | 门店唯一标识 | +| 门店名称 | name | 否 | String(64) | 腾讯大厦腾大餐厅 | 门店名称 | +| 门店行政区划码 | area_code | 否 | String(6) | 440305 | 门店所在地行政区划码 | +| 门店详细地址 | address | 否 | String(128) | 科技园中一路腾讯大厦 | 门店详细地址 | + + + + +**返回参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 返回状态码 | resp_code | M | 无 | string | 0 - 成功,非0 - 失败。注:此字段是通信标识,业务状态要看result_code | +| 返回错误信息 | resp_msg | [C](#eFgvt) | 无 | string | 错误描述:resp_code非0时返回 | +| 业务结果 | result_code | M | 1 | string | 0 - 成功,非0 - 失败 | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| 错误码描述 | error_msg | O | 无 | string | 错误信息描述 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 通道商户号 | sub_merchant_id | M | 无 | string | 微信、支付宝、QRC商户号 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 订单状态 | status | M | 无 | string | [详见下方订单状态](#aIxZP) | +| 乐刷订单号 | leshua_order_id | M | 32 | string | 乐刷订单号 | +| 支付类型 | pay_way | M | 无 | string | [详见下方支付类型](#nlHUH) | +| 支付完成时间 | pay_time | C | 无 | string | 支付成功时才返回 | +| 银行类型 | bank_type | O | 无 | string | 使用银行卡支付以外的(如零钱)全部展示为others,使用银行卡支付的详见下方[付款银行类型](#cX4pl) | +| 用户openid | openid | C | 无 | string | 支付成功时才返回 | +| 微信、支付宝等订单号 | out_transaction_id | C | 无 | string | 支付成功时才返回 | +| 用户子标识 | sub_openid | C | 无 | string | 支付成功时才返回。微信:公众号APPID下用户唯一标识;支付宝:买家的支付宝用户ID | +| 附加数据 | attach | O | 96 | string | 支付成功原样返回;注意:只能是汉字、英文字母、数字 | +| 交易类型 | trade_type | C | 无 | string | 支付成功时才返回:MICROPAY-条码支付,NATIVE-原生扫码支付,JSAPI-公众号支付、服务窗支付,SmPgPay-小程序支付,JSAPIQuick-简易支付 | +| 通道订单号 | channel_order_id | C | 无 | string | 支付成功时才返回 | +| 通道订单时间 | channel_datetime | C | 无 | string | 支付成功时才返回 | +| 支付宝红包金额 | coupon | C | 无 | string | 支付成功时才返回,单位(分) | +| 应结算金额 | settlement_amount | C | 无 | string | 实际结算金额,支付成功时才返回,单位(分) | +| 折扣优惠金额 | discount_amount | C | 无 | string | [订单优惠金额](#6QKR6),支付成功时才返回,单位(分) | +| 优惠详情 | promotion_detail | O | 无 | string | 银联单品的具体优惠详情;
微信优惠详情说明:[微信官网](https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_101&index=1) | +| 活动标志 | active_flag | O | 无 | string | 活动标志。WXLZ:微信绿洲;ZFBLH:支付宝蓝海 | +| 买家实付金额 | buyer_pay_amount | C | 无 | String | 微信、支付宝此值有效 | +| 请求耗时 | cost_time | C | 无 | string | 请求接口耗费的时间 | +| 通道标记 | channel_flag | O | 32 | string | GXST:高校食堂
PUBWEL:公益
PUBPAY:公缴
ZFBLH:支付宝新蓝海
SLZXY:私立中小幼
INS:保险
ONLINE:线上 | +| 银联/网联状态码 | channel_code | C | 无 | JSON | 透传银联的返回码。
银联支付宝:code, sub_code 银联微信:return_code, result_code,err_code 银联二维码平台:respCode 条码前置平台:status、code、result_code、err_code。
以上各种code,仅在银联返回时透传。
格式: | +| | alipay_infos | | | | 见下alipay_infos参数表 | + + +alipay_infos参数表 + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| | total_amount | M | 11 | Price | 交易金额(单位:分, 类型:数字) | +| | settle_amount | C | 11 | String | 结算币种订单金额 | +| | pay_amount | C | 11 | String | 支付币种订单金额 | +| | receipt_amount | M | 11 | String | 实收金额(单位:分, 类型:数字) | +| | point_amount | C | 11 | Price | 使用集分宝付款的金额 | +| | invoice_amount | C | 11 | Price | 交易中可给用户开具发票的金额(单位:元) | +| | fund_bill_list | M | | TradeFundBill[] | 交易支付使用的资金渠道。只有在签约中指定需要返回资金明细,或者入参的query_options 中指定时才返回该字段信息。 | +| | card_balance | C | 11 | Price | 支付宝卡余额 | +| | store_name | C | 512 | String | 发生支付交易的商户门店名称 | +| | discount_goods_detail | C | 1024 | String | 本次交易支付所使用的单品券优惠的商品优惠信息。只有在 query_options中指定时才返回该字段信息。 | +| | async_payment_mode | C | 20 | String | 异步支付模式,先享后付业务会返回该参 数 , 目 前 有 三 种 值 :ASYNC_DELAY_PAY( 异 步 延 时 付款); ASYNC_REALTIME_PAY( 异步准实时付款); SYNC_DIRECT_PAY(同步直接扣款); | +| | purchase_merchant_contribute | C | 8 | Price | 如果使用的这张券是用户购买的,则该字段代表用户在购买这张券时商户优惠的金额 | +| | auth_trade_pay_mode | C | 64 | String | 预授权支付模式,该参数仅在信用预授权支付场景下返回。信用预授权支付:CREDIT_PREAUTH_PAY | +| | advance_amount | C | 11 | String | 先享后付 2.0 垫资金额,不返回表示没有走垫资,非空表示垫资支付的金额 | +| | business_params | C | 512 | String | 商户传入业务信息,具体值要和支付宝约定将商户传入信息分发给相应系统,应用于安全,营销等参数直传场景格式为 json 格式 | +| | buyer_user_type | C | 18 | String | 买家用户类型。 CORPORATE:企业用户;PRIVATE:个人用户。 | +| | buyer_user_name | C | 128 | String | 买家名称;买家为个人用户时为买家姓名,买家为企业用户时为企业名称; | +| | mdiscount_amount | C | 11 | String | 商家优惠金额 | +| | discount_amount | C | 11 | String | 平台优惠金额 | +| | charge_flags | C | 64 | String | 费率活动标识,当交易享受活动优惠费率时,返回该活动的标识;(1)蓝海活动优惠费率 0,值为 bluesea_1(2) 特 殊 行 业 优 惠 费 率 0 , 值 为industry_special_00(3) 特殊行业优惠费率千一,值为industry_special_01 | +| | buyer_logon_id | M | 100 | String | 买家支付宝账号 | + + + + +##### 订单优惠金额 +微信支付包含商户和微信给的优惠金额,支付宝支付只包含商户给的优惠金额,银联二维码支付只包含商户给的优惠金额 + + + +**业务拓展参数示例** + +```json +"extend_business_params": "{ " + extend_params ":{ " + sys_service_provider_id ":" + 208812333665551 ", " + food_order_type ":" + home_delivery " } }", +``` + + + +**银联单品营销请求示例** + +```http +请求地址?amount=100&auth_code=XXXXXXXXXXXXXXXX&goods_detail= +%7b%22goodsInfo%22%3a%5b%7b%22id%22%3a%2212341234%22%2c%22name%22%3a%22%e6%b5%8b%e8%af%95%e5%95%86%e5%93%81%22%2c%22price%22%3a%22100%22%2c%22quantity%22%3a%221%22%7d%5d%7d +&merchant_id=XXXXXXXXXX&nonce_str=7aee8b722a614cfa9cfb39ad5409fcdf&service=upload_authcode&third_order_id=UB4C0D38F5091465F920200326100751& +sign=69fb04df6dda8bfd3110e4891efedc8b +``` + + + +##### 银联收款方附加数据 +price 和 quantity 字段为数字类型 + +```json +银联扫码交易时good_detail示例: +{ + "orderInfo" : { + "title" :"日用品", + "dctAmount" :"10000", + "addnInfo" :"屈臣氏(人民广场)店" + }, + "goodsInfo" : [ + {"id": "1234567890", + "name": "商品 1", + "price": 500, + "quantity": 1 + }, + { + "id": "1234567891", + "name": "商品 2", + "price": 1000, + "quantity": 2, + "category": "类目 1", + "addnInfo": "商品图片 http://www.95516.com/xxx.jpg" } ] +} +``` + +注: + +1. 银联扫码点餐时,在该域的订单信息 orderInfo 子域中的附加信息 addnInfo 字段需填写该门店对应的品牌标识、门店标识、桌号(其中 SaaS 收银的支付交易需有 品牌标识、门店标识,扫码点餐的支付交易必须有品牌标识、门店标识、桌号)。addnInfo 字段的参考格 式为:{brandId=值&storeId=值&tableNo=值},例如: addnInfo={brandId=AXMX&storeId=12345&tableNo=12} +2. 银联单品营销时,按照银联需要的内容,在该域的商品信息goodsInfo上送对应的商品信息。 + + + +**注意事项** + +(1)所有涉及到金额的单位都位分,币种为RMB,最小为1分,不允许出现小数 + +(2)文档中参数,标识为必填必须上送(如果缺少会报错),非必填的可填可不填 + +(3)返回参数中必填为是的参数是一定会返回的,必填为否的参数则不一定返回,因升级或配置等情况实际返回参数可能不会跟文档完全一致,必须以实际接收到的参数为准 + + + + + +### 3.扫码支付结果 +#### 3.1、扫码支付结果通知 +**由于诸多因素都会影响到支付结果的通知,推送可能存在延迟,并且不能保证100%推送成功。故强烈建议主接交易查询接口。 ** + +**POST Content-Type: text/xml; charset=utf-8 ** + +**通知参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| sign_type | sign_type | O | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使 用MD5算法) | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 商户号 | merchant_id | M | 10 | string | 乐刷商户号 | +| 通道商户号 | sub_merchant_id | M | 无 | string | 微信、支付宝、QRC商户号 | +| 乐刷订单号 | leshua_order_id | M | 32 | string | | +| 商户订单号 | third_order_id | M | 64 | string | | +| 订单金额 | amount | M | 无 | string | | +| 订单状态 | status | M | 无 | string | [详见下方订单状态](#aIxZP) | +| 支付类型 | pay_way | M | 无 | string | [详见下方支付类型](#Mpdey) | +| 支付时间 | pay_time | M | 无 | string | '2020-09-15 10:11:30' | +| 银行类型 | bank_type | O | 无 | string | 使用银行卡支付以外的(如零钱)全部展示为others,使用银行卡支付的详见下方[付款银行类型](#cX4pl) | +| 用户openid | openid | O | 无 | string | | +| 微信、支付宝等订单号 | out_transaction_id | O | 无 | string | | +| 用户子标识 | sub_openid | C | 无 | string | 支付成功时才返回。微信:微信公众号APPID 下用户唯一标识;支付宝:买家的支付宝用户ID | +| 附加数据 | attach | O | 96 | string | 原样返回;注意:只能是汉字、英文字母、数字 | +| 交易类型 | trade_type | C | 无 | string | 支付成功时才返回:MICROPAY-条码支付,NATIVE-原生扫码支付,JSAPI-公众号支付、服务窗支付,SmPgPay-小程序支付,JSAPIQuick-简易支付 | +| 通道订单号 | channel_order_id | C | 无 | string | 支付成功时才返回 | +| 通道订单时间 | channel_datetime | C | 无 | string | 支付成功时才返回 | +| 支付宝红包金额 | coupon | C | 无 | string | 支付成功时才返回 | +| 应结算金额 | settlement_amount | C | 无 | string | 实际结算金额,支付成功时才返回 | +| 折扣优惠金额 | discount_amount | C | 无 | string | 本订单优惠金额,支付成功时才返回 | +| 优惠详情 | promotion_detail | O | 无 | string | 银联单品的具体优惠详情;微信优惠详情说明:[微信官网](https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_101&index=1) | +| 活动标志 | active_flag | O | 无 | string | 活动标志:WXLZ:微信绿洲 ZFBLH:支付宝蓝海 | +| 买家实付金额 | buyer_pay_amount | O | 无 | String | 微信、支付宝此值有效 | +| 付款方附加数据 | iss_addn_data | M | 无 | String | 格式如下 | +| 设备编号 | device_info | O | 32 | string | 上游返回时才返 | +| 门店编号 | store_id | O | 32 | string | 上游返回时才返 | +| 通道标记 | channel_flag | O | 32 | string | GXST:高校食堂
PUBWEL:公益
PUBPAY:公缴
ZFBLH:支付宝新蓝海
SLZXY:私立中小幼
INS:保险
ONLINE:线上 | +| 花呗分期期数 | hb_fq_num | O | | string | 支付宝花呗分期数,一般为3、6、12期,乐刷透传,具体期数由支付宝侧限定 | +| 支付宝优惠信息 | alipay_infos | | | string | 见下alipay_infos参数表 | +| 分期信息 | install_info | O | 无 | string | 包含分期笔数(install_nums)/分期付款手续费率(install_rate)/商户补贴手续费率(merchant_fee_subsidy)) | + + +alipay_infos参数表 + +| **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | :---: | :---: | :---: | --- | +| total_amount | M | 11 | Price | 交易金额(单位:分, 类型:数字) | +| point_amount | O | 11 | Price | 积分支付的金额,单位为元,两位小数。该金额代表该笔交易中用户使用积分支付的金额,比如集分宝或者支付宝实时优惠等 | +| invoice_amount | O | 11 | Price | 交易中用户支付的可开具发票的金额,单位为元,两位小数。该金额代表该笔交易中可以给用户开具发票的金额 | +| receipt_amount | O | 11 | String | 实收金额(单位:分, 类型:数字) | +| discount_goods_detail | M | 1024 | String | 本次交易支付所使用的单品券优惠的商品优惠信息。只有在 query_options中指定时才返回该字段信息。 | +| mdiscount_amount | O | 11 | String | 商家优惠金额 | +| discount_amount | O | 11 | String | 平台优惠金额 | +| buyer_logon_id | M | 100 | String | 买家支付宝账号 | +| fund_bill_list | M | 256 | TradeFundBill[] | 交易支付使用的资金渠道。只有在签约中指定需要返回资金明细,或者入参的query_options 中指定时才返回该字段信息。 | + + +**注意:** + +1、支付发起后,乐刷一般30s左右才会返回支付通知,如需快速获取支付结果,请调用“交易结果查询”接口发起查询。 + +2、支付通知的频率:0s/15s/30s/1m/4m/34m/64m/94m/124m/184m + + + +##### 支付类型 +| 类型 | 说明 | 状态 | +| --- | --- | --- | +| WXZF | 微信 | 支持 | +| ZFBZF | 支付宝 | 支持 | +| UPSMPAY | 银联二维码 | 支持 | + + + + +**iss_addn_data参考格式:** + +| **参数名** | **变量名** | **类型** | **说明** | +| :---: | :---: | :---: | --- | +| 商品优惠信息 | dctDetail | String | 商品优惠明细 | +| 商品编号 | id | String | 参与优惠的商品编号 | +| 优惠活动编号 | dctId | String | 优惠活动的编号 | +| 商品优惠数量 | dctQuantity | String | 参与优惠的商品数量 | +| 商品优惠金额 | dctPrice | String | 商品的优惠总金额(分) | + + +```json +issAddnData= { + "dctDetail" : [{ + "id": "1234567890", + "dctId": "app012345", + "dctQuantity": 1", + "dctPrice": "500" + },{ + "id": "1234567891", + "dctId": "app012345", + "dctQuantity": "2", + "dctPrice": "1000", +``` + + + +**返回参数** + +| 返回内容 | 说明 | +| --- | --- | +| 000000 | 通知成功,乐刷收到此返回后不会继续通知。返回内容000000是string类型,没有双引号。示例:return 000000; | +| 其他 | 乐刷收到回复或者未收到回复,系统会再次通知。为避免资源浪费,请核实并确保响应信息的准确性,可以是否重复通知为检查手段 | + + + + +注意: + +1.回调地址为统一下单或条码支付的通知地址。 + +2.交易回调处理时间为5S内。 + +3.支付成功才进行推送。 + + + +**付款银行类型** + +| 通道 | 说明 | +| --- | --- | +| 微信 | [https://pay.weixin.qq.com/wiki/doc/api/mch_bank.php?chapter=4_2](https://pay.weixin.qq.com/wiki/doc/api/mch_bank.php?chapter=4_2) | +| 支付宝 | [https://alipay.open.taobao.com/doc2/detail?treeId=26&articleId=103259&docType=1](https://alipay.open.taobao.com/doc2/detail?treeId=26&articleId=103259&docType=1) | +| 银联二维码 | 1 - 借记卡 2- 贷记卡 | + + +#### 3.2、扫码交易结果查询 + + +**请求地址** + +(1)/cgi-bin/lepos_pay_gateway.cgi + +(2)测试商户信息及请求密钥和回调密钥请联系我们获取。 + +(3)生产环境走完协议流程并且联调完提交测试报告后,我司运营同事将会提供参数给贵司 + +(4)注:查询时,除非返回明确的支付成功、支付失败、订单关闭等最终状态,其他错误或者未支付状态,都需要再查询,直到得到明确的支付状态。如果超过一段时间还是未支付,可以调用关闭订单接口。 + +**请求参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 接口名 | service | M | 无 | string | query_status(此为固定值) | +| sign_type | sign_type | O | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使用MD5算法) | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | O | 64 | string | 选填 | +| 乐刷订单号 | leshua_order_id | O | 16 | string | third_order_id和leshua_order_id必填1个,优先使用leshua_order_id | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | + + + + +**返回参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 返回状态码 | resp_code | M | 无 | string | 0 - 成功,非0 - 失败。注:此字段是通信标识,业务状态要看result_code | +| 返回错误信息 | resp_msg | C | 无 | string | 错误描述:resp_code非0时返回 | +| 业务结果 | result_code | M | 1 | string | 0 - 成功,非0 - 失败 | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| 错误码描述 | error_msg | O | 无 | string | 错误信息描述 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 通道商户号 | sub_merchant_id | M | 无 | string | 微信、支付宝、QRC商户号 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 订单状态 | status | M | 无 | string | [详见下方订单状态](#aIxZP) | +| 乐刷订单号 | leshua_order_id | M | 32 | string | 乐刷订单号 | +| 订单金额 | amount | M | 无 | string | | +| 支付类型 | pay_way | M | 无 | string | [详见下方支付类型](#nlHUH) | +| 支付完成时间 | pay_time | C | 无 | string | 支付成功时才返回 | +| 银行类型 | bank_type | O | 无 | string | 使用银行卡支付以外的(如零钱)全部展示为others,使用银行卡支付的详见下方[付款银行类型](#cX4pl) | +| 用户openid | openid | C | 无 | string | 支付成功时才返回 | +| 微信、支付宝等订单号 | out_transaction_id | C | 无 | string | 支付成功时才返回 | +| 用户子标识 | sub_openid | C | 无 | string | 支付成功时才返回。微信:微信公众号APPID下用户唯一标识;支付宝:买家的支付宝用户ID | +| 附加数据 | attach | O | 96 | string | 支付成功时原样返回;注意:只能是汉字、英文字母、数字 | +| 交易类型 | trade_type | C | 无 | string | 支付成功时才返回:MICROPAY-条码支付,NATIVE-原生扫码支付,JSAPI-公众号支付、服务窗支付,SmPgPay-小程序支付,JSAPIQuick-简易支付 | +| 通道订单号 | channel_order_id | C | 无 | string | 支付成功时才返回 | +| 通道订单时间 | channel_datetime | C | 无 | string | 支付成功时才返回 | +| 支付宝红包金额 | coupon | C | 无 | string | 支付成功时才返回,单位(分) | +| 应结算金额 | settlement_amount | C | 无 | string | 实际结算金额,支付成功时才返回 | +| 折扣优惠金额 | discount_amount | C | 无 | string | 订单优惠金额,支付成功时才返回 | +| 优惠详情 | promotion_detail | O | 无 | string | 银联单品的具体优惠详情;

微信优惠详情说明:[微信官方文档](https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_101&index=1) | +| 活动标志 | active_flag | O | 无 | string | 活动标志。WXLZ:微信绿洲;ZFBLH:支付宝蓝海 | +| 买家实付金额 | buyer_pay_amount | O | 无 | String | 微信、支付宝此值有效 | +| 请求耗时 | cost_time | C | 无 | string | 请求接口耗费的时间 | +| 退款金额
| refund_amount | O | 无 | string | 订单已退款的金额 | +| 简易支付标识 | simple_url_flag | O | 无 | string | 1表示是简易支付,0表示不是简易支付 | +| 设备编号 | device_info | O | 32 | string | 上游返回时才返 | +| 门店编号 | store_id | O | 32 | string | 上游返回时才返 | +| 支付中间态补充 | interm_state | O | 32 | O | 目前仅针对微信支付,订单为支付中时返回:
NOTPAY,USERPAYING | +| 通道标记 | channel_flag | O | 32 | string | GXST:高校食堂
PUBWEL:公益
PUBPAY:公缴
ZFBLH:支付宝新蓝海
SLZXY:私立中小幼
INS:保险
ONLINE:线上 | +| 花呗分期期数 | hb_fq_num | O | | string | 支付宝花呗分期数,一般为3、6、12期,乐刷透传,具体期数由支付宝侧限定 | +| | alipay_infos | | | | 见下alipay_infos参数表 | + + +alipay_infos参数表 + +| **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | :---: | :---: | :---: | --- | +| total_amount | M | 11 | Price | 交易金额(单位:分, 类型:数字) | +| point_amount | O | 11 | Price | 积分支付的金额,单位为元,两位小数。该金额代表该笔交易中用户使用积分支付的金额,比如集分宝或者支付宝实时优惠等 | +| invoice_amount | O | 11 | Price | 交易中用户支付的可开具发票的金额,单位为元,两位小数。该金额代表该笔交易中可以给用户开具发票的金额 | +| receipt_amount | O | 11 | String | 实收金额(单位:分, 类型:数字) | +| discount_goods_detail | M | 1024 | String | 本次交易支付所使用的单品券优惠的商品优惠信息。只有在 query_options中指定时才返回该字段信息。 | +| mdiscount_amount | O | 11 | String | 商家优惠金额 | +| discount_amount | O | 11 | String | 平台优惠金额 | +| buyer_logon_id | M | 100 | String | 买家支付宝账号 | +| fund_bill_list | M | 256 | TradeFundBill[] | 交易支付使用的资金渠道。只有在签约中指定需要返回资金明细,或者入参的query_options 中指定时才返回该字段信息。 | + + + + + + + + +**注意事项** + +(1)所有涉及到金额的单位都位分,币种为RMB,最小为1分,不允许出现小数 + +(2)文档中参数,标识为必填必须上送(如果缺少会报错),非必填的可填可不填 + +(3)返回参数中必填为是的参数是一定会返回的,必填为否的参数则不一定返回,因升级或配置等情况实际返回参数可能不会跟文档完全一致,必须以实际接收到的参数为准 + + + +### +### 4、扫码退款 +#### 4.1、扫码退款接口 + +**注意:超过三个月的订单,无法退款请注意 +****一个交易订单号,最多支持发起50次退款** +**请求地址** + +(1)/cgi-bin/lepos_pay_gateway.cgi + +(2)测试商户信息及请求密钥和回调密钥请联系我们获取。 + +(3)生产环境走完协议流程并且联调完提交测试报告后,我司运营同事将会提供参数给贵司 + +(4)退款一般会返回退款中的状态,如需获取真实退款状态,请调用退款查询接口 + +(5)每笔支付订单部分退款次数不能超过50次 + +(6)退款有效期为60天,即订单发起后的60天内可以退款 + + + +**请求参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 接口名 | service | M | 无 | string | unified_refund 注:此为固定值 | +| sign_type | sign_type | O | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使用MD5算法) | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | O | 64 | string | | +| 乐刷订单号 | leshua_order_id | O | 32 | string | 商户订单号跟乐刷订单号必填1个,优先使用leshua_order_id | +| 商户退款单号 | merchant_refund_id | M | 64 | string | 可以包含字母(不能有“_”等特殊字符),确保同一商户下唯一 | +| 附加数据 | attach | O | 96 | string | 退款成功原样返回;注意:只能是汉字、英文字母、数字 | +| 退款金额 | refund_amount | M | 10 | string | 退款金额,可做部分退款 | +| 退款类型 | refund_type | O | 否 | string | 0:不使用账户余额退款,
1:可以使用账户余额退款(优先使用正向交易抵扣,正向交易不够抵扣情况下可用账户余额进行退款)。
2:限定只可以用账户余额进行退款
不传默认不使用账户余额退款 (注意: 实时清分的只能使用账户退款 ) | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串,长度要求32位以内 | +| 通知地址 | notify_url | O | 128 | string | 接收乐刷退款结果通知的URL,需做Url Encode处理,需要绝对路径,确保乐刷能正确访问 | +| 签名 | sign | M | 32 | string | MD5签名结果 | + + + + +返回参数 + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 返回状态码 | resp_code | M | 无 | string | 0 - 成功,非0 - 失败。注:此字段是通信标识,业务状态要看result_code | +| 返回错误信息 | resp_msg | C | 无 | string | 错误描述:resp_code非0时返回 | +| 业务结果 | result_code | M | 1 | string | 0 - 成功,非0 - 失败 | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| 错误码描述 | error_msg | O | 无 | string | 错误信息描述 | +| 附加数据 | attach | O | 96 | string | 退款成功原样返回;注意:只能是汉字、英文字母、数字 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| | | | | | | +| 通道商户号 | sub_merchant_id | M | 无 | string | 微信、支付宝、QRC商户号 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 订单状态 | status | M | 无 | string | [详见下方订单状态](#aIxZP) | +| 乐刷订单号 | leshua_order_id | M | 32 | string | 乐刷订单号 | +| 退款金额 | refund_amount | M | 无 | string | 退款金额 | +| 商户退款id | merchant_refund_id | M | 64 | string | | +| 乐刷退款id | leshua_refund_id | M | 32 | string | | +| 订单总金额 | total_amount | M | 无 | string | 订单总金额 | +| 订单余额 | order_balance | M | 无 | string | 订单余额:本次退款后订单剩余金额 | +| 实际退款金额 | settlement_refund_amount | O | 无 | string | 实际退款金额 | +| 折扣优惠退款金额 | discount_refund_amount | O | 无 | string | 折扣优惠退款金额 | +| 退款优惠详情 | refund_detail | O | 无 | string | 退款优惠详情 | +| 请求耗时 | cost_time | C | 无 | string | 请求接口耗费的时间 | + + + + + + + + +#### 4.2、扫码退款查询 + + +**请求地址** + +(1)/cgi-bin/lepos_pay_gateway.cgi + +(2)测试商户信息及请求密钥和回调密钥请联系我们获取。 + +(3)生产环境走完协议流程并且联调完提交测试报告后,我司运营同事将会提供参数给贵司 + +(4)注: 此接口如果查询到退款状态为中间状态时,会同步向上游查询一次退款状态;如果查询到是退款异常情况时,会同步向上游重新发起一次退款请求。 + + + +**请求参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 接口名 | service | M | 无 | string | unified_query_refund 注:此为固定值 | +| sign_type | sign_type | O | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使用MD5算法) | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | O | 64 | string | | +| 乐刷订单号 | leshua_order_id | O | 32 | string | 跟third_order_id必填1个 | +| 商户退款单号 | merchant_refund_id | O | 64 | string | | +| 乐刷退款单号 | leshua_refund_id | O | 32 | string | 跟merchant_refund_id必填1个 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | + + + + +**返回参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 返回状态码 | resp_code | M | 无 | string | 0 - 成功,非0 - 失败。注:此字段是通信标识,业务状态要看result_code | +| 返回错误信息 | resp_msg | C | 无 | string | 错误描述:resp_code非0时返回 | +| 业务结果 | result_code | M | 1 | string | 0代表成功,非0代表失败 | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| 错误码描述 | error_msg | O | 无 | string | 错误信息描述 | +| 附加数据 | attach | O | 96 | string | 原样返回;注意:只能是汉字、英文字母、数字 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 通道商户号 | sub_merchant_id | M | 无 | string | 微信、支付宝、QRC商户号 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 订单状态 | status | M | 无 | string | [详见下方订单状态](#aIxZP) | +| 乐刷订单号 | leshua_order_id | M | 32 | string | 乐刷订单号 | +| 退款金额 | refund_amount | M | 无 | string | 退款金额 | +| 商户退款id | merchant_refund_id | M | 64 | string | | +| 乐刷退款id | leshua_refund_id | M | 32 | string | | +| 订单总金额 | total_amount | M | 无 | string | | +| 退款时间 | refund_time | O | 无 | string | 退款成功时间:退款成功才返回 | +| 实际退款金额 | settlement_refund_amount | O | 无 | string | 实际退款金额 | +| 折扣优惠退款金额 | discount_refund_amount | O | 无 | string | 折扣优惠退款金额 | +| 退款优惠详情 | refund_detail | O | 无 | string | 退款优惠详情 | +| 请求耗时 | cost_time | C | 无 | string | 请求接口耗费的时间 | + + + + + + +#### 4.3、扫码退款结果通知 +**由于诸多因素都会影响到支付结果的通知,推送可能存在延迟,并且不能保证100%推送成功。故强烈建议主接交易查询接口。** + +**通知参数** + +| **字段名** | **变量名** | **必填** | **长度限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| sign_type | sign_type | O | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使用MD5算法) | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 订单状态 | status | M | 无 | string | [详见下方订单状态](#aIxZP) | +| 失败原因 | failure_reason | O | 无 | string | 失败原因 | +| 附加数据 | attach | O | 96 | string | 原样返回;注意:只能是汉字、英文字母、数字 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 通道商户号 | sub_merchant_id | M | 无 | string | 微信、支付宝、QRC商户号 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 乐刷订单号 | leshua_order_id | M | 32 | string | 乐刷订单号 | +| 退款金额 | refund_amount | M | 无 | string | 退款金额 | +| 商户退款id | merchant_refund_id | M | 64 | string | | +| 乐刷退款id | leshua_refund_id | M | 32 | string | | +| 订单总金额 | total_amount | M | 无 | string | | +| 退款时间 | refund_time | C | 无 | string | 退款成功时间:退款成功才返回 | +| 折扣优惠退款金额 | discount_refund_amount | O | 无 | String | 折扣优惠退款金额 | +| 实际退款金额 | settlement_refund_amount | O | 无 | String | 实际退款金额 | +| 退款优惠详情 | refund_detail | O | 无 | String | 退款优惠详情(**在退款成功后才返回**) | + + + + +**注意:** + +1、发起退款请求后,乐刷一般30s左右才会返回支付通知,如需快速获取支付结果,请调用“退款”接口发起查询。 + + + +**返回参数** + +| 返回内容 | 说明 | +| --- | --- | +| 000000 | 通知成功,乐刷收到此返回后不会继续通知 | +| 其他 | 乐刷收到回复或者未收到回复,系统会再次通知。为避免资源浪费,请核实并确保响应信息的准确性,可以是否重复通知为检查手段 | + + + + + + +### 5、扫码订单关闭接口 +**请求地址** + +(1)/cgi-bin/lepos_pay_gateway.cgi + +(2)测试商户信息及请求密钥和回调密钥请联系我们获取。 + +(3)生产环境走完协议流程并且联调完提交测试报告后,我司运营同事将会提供参数给贵司 + +(4)统一条码支付的订单后15秒后才允许调用订单关闭接口,统一下单订单无此限制。 +(5)银联二维码的不支持关单 + + + +**请求参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 接口名 | service | M | 无 | string | close_order(此为固定值) | +| sign_type | sign_type | O | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法;(不上送默认为使用MD5算法) | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | O | 64 | string | 选填 | +| 乐刷订单号 | leshua_order_id | O | 16 | string | third_order_id和leshua_order_id必填1个,优先使用leshua_order_id | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | + + + + +**返回参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 返回状态码 | resp_code | M | 无 | string | 0 - 成功,非0 - 失败。注:此字段是通信标识,业务状态要看result_code | +| 返回错误信息 | resp_msg | C | 无 | string | 错误描述:resp_code非0时返回 | +| 业务结果 | result_code | M | 1 | string | 0 - 成功,非0 - 失败 | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| 错误码描述 | error_msg | O | 无 | string | 错误信息描述 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 用户子标识 | sub_openid | M | 无 | string | | +| 订单状态 | status | M | 无 | string | | +| 乐刷订单号 | leshua_order_id | M | 32 | string | 乐刷订单号 | +| 交易类型 | pay_way | M | 无 | string | [详见下方支付类型](#nlHUH) | +| 订单金额 | amount | M | 无 | string | | +| 请求耗时 | cost_time | C | 无 | string | 请求接口耗费的时间 | + + +##### 订单状态 +| **值** | **说明** | +| :--- | :--- | +| 0 | 支付中 | +| 2 | 支付成功 | +| 6 | 订单关闭 | +| 8 | 支付失败 | +| 10 | 退款中 | +| 11 | 退款成功 | +| 12 | 退款失败 | + + + + +### 6、授权码 +> 授权码相关接口与**条码支付接口**配套使用 +> + +#### 6.1、授权码查询用户openid +**** + +**请求地址** + +(1)/cgi-bin/lepos_pay_gateway.cgi + +(2)测试商户信息及请求密钥和回调密钥请联系我们获取。 + +(3)生产环境走完协议流程并且联调完提交测试报告后,我司运营同事将会提供参数给贵司 + +(4)注: 此接口需要上送户付款码和对应的微信公众号appid才能查询到用户openid + + + +**请求参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 接口名 | service | M | 无 | string | query_openid 注:此为固定值 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 授权码 | auth_code | M | 32 | string | 微信授权码 | +| 微信appid | appid | M | 32 | string | 微信公众号ID | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | + + + + +**返回参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 返回状态码 | resp_code | M | 无 | string | 0 - 成功,非0 - 失败。注:此字段是通信标识,业务状态要看result_code | +| 返回错误信息 | resp_msg | C | 无 | string | 错误描述:resp_code非0时返回 | +| 业务结果 | result_code | M | 1 | string | 0 - 成功,非0 - 失败 | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| 错误码描述 | error_msg | O | 无 | string | 错误信息描述 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 用户子标识 | sub_openid | M | 无 | string | | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 微信用户标识 | openid | M | 无 | string | 乐刷appid下的用户微信openid | + + + + + + +#### 6.2、授权码获取银联云闪付用户ID + + +**简要描述** + +(1)使用用户授权码换取银联云闪付用户ID,使用此ID发起银联JS支付 + +**注:** 此步骤的前置条件:与公司归属银联分公司签署协议,并添加域名白名单获取用户授权码。 + + + +**请求参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| :--- | :--- | :--- | --- | --- | --- | +| 接口名 | service | M | 无 | string | query_userid(此为固定值) | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 用户授权码 | user_auth_code | M | 无 | string | | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 银联支付标识 | app_up_identifier | C | | | 收款方识别HTTP请求User Agent中包含银联支付标识,格式为“UnionPay/<版本号> ”,注意APP标识仅支持字母和数字。云闪付app支付不填,云闪付外app必填。示例:UnionPay/1.0 ICBCeLife | +| 签名 | sign | M | 32 | string | MD5签名结果 | + + + + +**返回参数** + +| **参数名** | **变量名** | **必填** | **限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 返回状态码 | resp_code | M | 无 | string | 0 - 成功,非0 - 失败。注:此字段是通信标识,业务状态要看result_code | +| 返回错误信息 | resp_msg | O | 无 | string | 错误描述:resp_code非0时返回 | +| 业务结果 | result_code | M | 1 | string | 0 - 成功,非0 - 失败 | +| 错误码 | error_code | O | 无 | string | [参考错误码信息表](#rOC0m) | +| 错误码描述 | error_msg | O | 无 | string | 错误信息描述 | +| 商户号 | merchant_id | M | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | M | 64 | string | 商户内部订单号 | +| 随机字符串 | nonce_str | M | 32 | string | 随机字符串 | +| 签名 | sign | M | 32 | string | MD5签名结果 | +| 用户ID | user_id | O | 无 | string | | + + + + + + +##### 支付类型 +| 类型 | 说明 | 状态 | +| --- | --- | --- | +| WXZF | 微信 | 支持 | +| ZFBZF | 支付宝 | 支持 | +| UPSMZF | 银联二维码 | 支持 | +| DCPAY | 数字货币 | 支持 | + + + + +##### 订单状态 +| **值** | **说明** | +| :--- | :--- | +| 0 | 支付中 | +| 2 | 支付成功 | +| 6 | 订单关闭 | +| 8 | 支付失败 | +| 10 | 退款中 | +| 11 | 退款成功 | +| 12 | 退款失败 | + + + + +##### 付款银行类型 +| **通道** | **说明** | +| --- | --- | +| 微信 | [https://pay.weixin.qq.com/wiki/doc/api/mch_bank.php?chapter=4_2](https://pay.weixin.qq.com/wiki/doc/api/mch_bank.php?chapter=4_2) | +| 支付宝 | [https://alipay.open.taobao.com/doc2/detail?treeId=26&articleId=103259&docType=1](https://alipay.open.taobao.com/doc2/detail?treeId=26&articleId=103259&docType=1) | +| 银联二维码 | 1 - 借记卡 2- 贷记卡 | + + + + +##### 业务拓展参数示例(支付宝小程序需传) +```json +"extend_business_params": "{ " +business_params ": { + "bizSource": "mAppQrFood", + "sourcePath": "pageFoodChain/shopTakeOut/main?suid=10" + }, + "goods_detail": [{ + "goods_id": "5544", + "goods_name": "爽口青笋丝", + "price": "0.50", + "quantity": 2, + "goods_category": "888", + "categories_tree": "888", + "body": "爽口青笋丝", + "show_url": "pageFoodChain/storeDetail/main?suid=10" + }], "extend_params": { + "sys_service_provider_id": "208812333665551", + "food_order_type": "home_delivery" + } +}", +``` + + + +**条码支付接口goods_detail,按银联单品营销格式传递****参数goodsInfo,格式如下** + + + +```json + "goods_detail":{ + "goodsInfo" : [ + { + "id": "1234567890", + "name": "商品 1", + "price": "500", + "quantity": "1"}, + { + "id": "1234567891", + "name": "商品 2", + "price": "1000", + "quantity": "2", + "category": "类目 1", + "addnInfo": "商品图片 http://www.95516.com/xxx.jpg"}] + }, +``` + + + +## 7、刷卡及扫码支付异步通知 + + +采用HTTP 标准的POST协议(请使用 application/x-www-form-urlencoded 此Content-Type接收通知消息),为了保证数据正确性、准确性,数据必须验证签名。 + + + +**返回数据(支付成功通知如下)** + +```xml + + 0 + 1 + 0 + ALIPAYACCOUNT + + 2018-12-07 22:42:39 + 7551000001201812073279154152 + 0 + 0 + + 1812072242293655 + 0000000018 + 2088032562556921 + 2018120722001456935441223102 + 2018-12-07 22:42:39 + ZFBZF + 0 + 1 + 0b574d40e722ec76b55d4e8795f71182 + 2 + + 20181207224223102101 + NATIVE + +``` + + + +**返回数据(刷卡支付成功通知如下)** + +```xml + +0 +600 +000000 +000010 + +890584058130851 +89147601 +000109 +2203221139307885 +220322393155923079 +2041000394 +6012514015 +20220322114213731 + + +113935096395 +2022-03-22 11:41:43 +21e70c91171b8d6ba00dea3c3ef6affa +218JCA8L7760 +32 +20220322114005352 +220322393155923079 + +``` + + + + + +**返回数据(扫码退款成功通知如下)** + +```xml + + 0 + + 0 + 90001401275201 + 90002391275201 + 9115219 + 0000091152192752007041753 + mccqvx9q0d5nifcsd0uv9xlmkxgr6kdg + 2020-07-04 17:53:24 + 1000 + 426adb68310b659953d18b629c1a2e7e + 11 + 208860057301617 + 0000091152192752007041749 + 5500 + +``` + + + +### 7.1 扫码支付成功通知 + + +(1)通知 URL 是代理商后台配置的通知地址。扫码支付成功后,乐刷会把相关支付信息和用户信息发送到该 URL,商户需要接收处理信息。对后台通知交互时,如果收到商户的应答不是成功或者超时,乐刷认为通知失败,会通过一定的策略定期重新发起通知,尽可能的提高通知的成功率,但是不保证通知最终一定能成功。由于存在重复发送后台通知的情况,因此同样的通知可能会多次发送给商户系统。系统必须能够正确处理重复的通知。 + +(2)验证签名方法:通知的 error_code、sign 字段不参与签名,签名方式参照数据格式及数据签名说明,其中签名 key 统一由乐刷提供。另外,组织签名串时空值也参与签名。 + + + +**通知参数** + +| **字段名** | **变量名** | **必填** | **长度限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 错误码 | error_code | 否 | 无 | string | 参照错误码信息参照表 | +| sign_type | sign_type | 否 | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法; | +| 签名 | sign | 是 | 32 | string | MD5 签名结果 | +| 商户号 | merchant_id | 是 | 10 | string | 乐刷商户号 | +| 乐刷订单号 | leshua_order_id | 是 | 32 | string | | +| 商户订单号 | third_order_id | 是 | 64 | string | | +| 订单金额 | amount | 是 | 无 | string | | +| 订单状态 | status | 是 | 无 | string | 见下方[订单的状态](#CozIx) | +| 支付类型 | pay_way | 是 | 无 | string | 见下方[支付类型](#yGR0s) | +| 支付时间 | pay_time | 是 | 无 | string | '2018-08-20 19:19:19' | +| 银行类型 | bank_type | 否 | 无 | string | | +| 用户openid | openid | 否 | 无 | string | | +| 微信、支付宝等订单号 | out_transaction_id | 否 | 无 | string | | +| 用户子标识 | sub_openid | 否 | 无 | string | 支付成功时才返回。微信:子商户appid 下用户唯一标识;支付宝:买家的支付宝用户id; | +| 附加数据 | attach | 否 | 96 | string | 支付成功时才返回。原样返回 | +| 交易类型 | trade_type | 否 | 无 | string | 支付成功时才返回。(1)MICROPAY--刷卡支付/条码支付(2)NATIVE--原生扫码支付(3)JSAPI--公众号支付/服务窗支付(4)APP--app支付(5)H5Pay-支付支付(6)SmPgPay-小程序支付(7)JSAPIQuick-简易支付 | +| 通道订单号 | channel_order_id | 否 | 无 | string | 支付成功时才返回 | +| 通道订单时间 | channel_datetime | 否 | 无 | string | 支付成功时才返回 | +| 支付宝红包金额 | coupon | 否 | 无 | string | 支付成功时才返回,单位:分 | +| 应结算金额 | settlement_amount | 否 | 无 | string | 实际结算金额,支付成功时才返回,单位:分 | +| 折扣优惠金额 | discount_amount | 否 | 无 | string | 本订单优惠金额,支付成功时才返回,单位:分 | +| 优惠详情 | promotion_detail | 否 | 无 | string | 优惠详情:[微信官网](https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_101&index=1) | +| 活动标志 | active_flag | 否 | 无 | string | 活动标志:WXLZ:微信绿洲,ZFBLH:支付宝蓝海 | +| **商户终端编号** | pos_no | 否 | 60 | string | 只能是汉字、英文字母、数字 注意:银联类型交易时,只能 8位:数字字母 不支持 特殊字符 | + + + + +**返回参数** + +| **字段名** | **说明** | +| --- | --- | +| 000000 | 通知成功,乐刷收到此返回后不会继续通知 | +| 其他 | 乐刷收到回复或者未收到回复,系统会再次通知。为避免资源浪费,请核实并确保响应信息的准确性,可以是否重复通知为检查手段。 | + + + + +##### 支付类型 +| **类型** | **说明** | **状态** | +| --- | --- | --- | +| WXZF | 微信 | 支持 | +| ZFBZF | 支付宝 | 支持 | +| UPSMZF | 银联二维码 | 支持 | +| QQZF | QQ | 支持 | +| POS | 刷卡 | 支持 | +| DCPAY | 数字货币 | 支持 | +| UPSMPAY | 银联二维码(回调时返回) | 支持 | + + + + +##### 订单状态值 +| **值** | **说明** | +| --- | --- | +| 0 | 支付中 | +| 2 | 支付成功 | +| 6 | 订单关闭 | +| 8 | 支付失败 | +| 10 | 退款中 | +| 11 | 退款成功 | +| 12 | 退款失败 | +| 30 | 刷卡支付成功 | +| 31 | 刷卡撤销成功 | +| 32 | 刷卡退货成功 | +| 33 | 刷卡冲正成功 | + + + + +### 7.2 扫码退款通知 + + +(1)通知 URL 与扫码支付成功通知一样,均为代理商后台配置的通知地址。扫码交易退款时、退款成功时、退款失败时,乐刷均会把相关退款信息发送到该 URL,商户需要接收处理信息。对后台通知交互时,如果收到商户的应答不是成功或者超时,乐刷认为通知失败,会通过一定的策略定期重新发起通知,尽可能的提高通知的成功率,但是不保证通知最终一定能成功。由于存在重复发送后台通知的情况,因此同样的通知可能会多次发送给商户系统。系统必须能够正确处理重复的通知。 + +(2)验证签名方法:通知的 error_code、sign 字段不参与签名,签名方式参照第 3 章 数据签名,其中签名 key 统一由乐刷提供。另外,组织签名串时空值也参与签名。 + + + +**通知参数** + +| **字段名** | **变量名** | **必填** | **长度限制** | **类型** | **说明** | +| --- | --- | --- | --- | --- | --- | +| 签名 | sign | 是 | 32 | string | MD5 签名结果 | +| sign_type | sign_type | 否 | 10 | string | 接口签名类型::MD5/SM3。 支持MD5和国密SM3算法 | +| 订单状态 | status | 是 | 无 | string | 见下方[订单状态](#yP5Xo) | +| 失败原因 | failure_reason | 否 | 无 | string | 失败原因 | +| 商户号 | merchant_id | 是 | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | 是 | 64 | string | 商户内部订单号 | +| 乐刷订单号 | leshua_order_id | 是 | 32 | string | 乐刷订单号 | +| 退款金额 | refund_amount | 是 | 无 | string | 退款金额(分) | +| 商户退款 id | merchant_refund_id | 是 | 64 | string | | +| 乐刷退款 id | leshua_refund_id | 是 | 32 | string | | +| 订单总金额 | total_amount | 是 | 无 | string | | +| 退款时间 | refund_time | 否 | 无 | string | 退款成功时间-退款成功才返回 | + + + + +**返回参数** + +| **字段名** | **变量名** | +| --- | --- | +| 000000 | 通知成功,乐刷收到此返回后不会继续通知 | +| 其他 | 乐刷收到回复或者未收到回复,系统会再次通知 | + + + + +##### 订单状态 +| **值** | **说明** | +| --- | --- | +| 0 | 支付中 | +| 2 | 支付成功 | +| 6 | 订单关闭 | +| 8 | 支付失败 | +| 10 | 退款中 | +| 11 | 退款成功 | +| 12 | 退款失败 | +| 30 | 刷卡支付成功 | +| 31 | 刷卡撤销成功 | +| 32 | 刷卡退货成功 | +| 33 | 刷卡冲正成功 | + + + + +### 7.3 刷卡交易通知 + + +(1)通知 URL 与扫码支付成功通知一样,均为代理商后台配置的通知地址。刷卡交易成功时、刷卡撤销成功时、刷卡退货成功时、刷卡冲正成功时,乐刷均会把相关交易信息发送到该 URL,商户需要接收处理信息。对后台通知交互时,如果收到商户的应答不是成功或者超时,乐刷认为通知失败,会通过一定的策略定期重新发起通知,尽可能的提高通知的成功率,但是不保证通知最终一定能成功。由于存在重复发送后台通知的情况,因此同样的通知可能会多次发送给商户系统。系统必须能够正确处理重复的通知。 + +(2)验证签名方法:通知的 error_code、sign 字段不参与签名,签名方式参照第 3 章 数据签名,其中签名 key 统一由乐刷提供。另外,组织签名串时空值也参与签名。 + +(3)境外卡统一按照贷记卡费率进行结算,所以返回2 + + + +**通知参数** + +| **字段名** | **变量名** | **必填** | **长度限制** | **类型** | **说明** | +| :---: | :---: | :---: | :---: | :---: | :---: | +| 签名 | sign | 是 | 32 | string | MD5 签名结果 | +| 订单状态 | status | 是 | 无 | string | 详见下方[订单状态](#p7QuH) | +| 结算类型 | T0 | 是 | 无 | string | | +| 设备SN号 | sn | 是 | 无 | string | 硬件序列号和机身上一致 | +| 授权码 | authorize_code | 是 | 6 | string | 普通交易下返回000000 预授权交易不固定 | +| 失败原因 | failure_reason | 否 | 无 | string | 失败原因 | +| 商户号 | merchant_id | 是 | 10 | string | 由乐刷分配 | +| 商户订单号 | third_order_id | 是 | 64 | string | 商户内部订单号
退货时,返回原订单的第三方订单号 | +| 乐刷订单号 | leshua_order_id | 是 | 32 | string | 乐刷订单号
退货时,返回原订单的乐刷订单号 | +| 交易金额 | amount | 是 | 无 | string | 交易金额(分) | +| 银行卡号 | card_id | 否 | 无 | string | 刷卡交易时提供 | +| 卡类型 | card_type | 否 | 1 | string | 1----借记卡
2----贷记卡 3---- 准贷记 | +| 发行主体 | pan_overseas | 否 | 1 | string | 0----境内卡 1----境外卡 | +| 结算方式 | pan_type | 否 | 1 | string | (1使用商户借记卡费率,非1使用商户贷记卡费率 ) 0-----未知 1-----借记 2-----贷记 3-----准贷记 4-----借贷合一 5-----预付费 6-----半开放预付费 | +| 发卡行 | bank_name | 否 | 无 | string | 刷卡交易时提供 | +| 刷卡pos批次号 | batch_no | 是 | 无 | string | 每次签到后从后台获取批次号,当批交易均上送此批次号 | +| 刷卡pos流水号 | flow_id | 是 | 3 | string | 终端为每个交易随机生成的流水号,随交易信息上送 | +| 乐刷SN号 | leshua_sn | 是 | 无 | string | 刷卡交易时提供 | +| 刷卡pos参考号 | reference_number | 是 | 32 | string | POS中心分配的流水号,作为对账参考号 | +| 银联商户号 | cups_merchant_id | 否 | 20 | string | 表示上送银联的商户编号 | +| 银联终端号 | cups_terminal_id | 否 | 8 | string | 表示上送银联的终端编号 | +| 交易流水号 | transaction_id | 否 | 18 | string | 用于表示此操作的流水号 | +| 刷卡交易时间 | pay_time | 否 | 无 | string | 刷卡交易时提供 | +| 消费撤销时间 | refund_time | 否 | 无 | string | 消费撤销时提供 | +| 消费退货时间 | returngoods_time | 否 | 无 | string | 消费退货时提供 | +| 消费冲正时间 | reverse_time | 否 | 无 | string | 消费冲正时提供 | +| 商户撤销id | merchant_refund_id | 否 | 无 | string | 消费撤销时提供
第三方传入的撤销订单号 | +| 商户退货id | merchant_returngoods_id | 否 | 无 | string | 消费退货时提供
第三方传入的退货订单号 | +| 乐刷退款id | leshua_refund_id | 否 | 18 | string | 消费退货时提供
乐刷生成的退货流水号 | +| 商户冲正id | merchant_reverse_id | 否 | 无 | string | 消费冲正时提供
第三方传入的冲正订单号 | +| 到账标识 | t0 | 否 | 1 | String | 0:表示t1到账 1:表示t0到账; 刷卡交易时提供 | + + + + +**返回参数** + +| **字段名** | **变量名** | +| --- | --- | +| 000000 | 通知成功,乐刷收到此返回后不会继续通知 | +| 其他 | 乐刷收到回复或者未收到回复,系统会再次通知 | + + + + +##### 订单状态 +| **值** | **说明** | +| --- | --- | +| 30 | 刷卡支付成功 | +| 31 | 刷卡撤销成功 | +| 32 | 刷卡退货成功 | +| 33 | 刷卡冲正成功 | +| 41 | 预授权成功 | +| 43 | 预授权撤销成功 | +| 45 | 预授权完成(请求)成功 | +| 47 | 预授权完成(通知)成功 | +| 49 | 预授权完成撤销成功 | + + +### 7.4 刷卡交易查询 + + +请求地址:/agentCardTransaction/cardFlowQuery + +加密规则: md5hex(代理商密钥+reqSerialNo+version+ data的 json 串) + + + +**请求参数:** + +| 名称 | 类型 | 是否必须 | 默认值 | 备注 | 其他信息 | +| --- | --- | --- | --- | --- | --- | +| version | string | 必须 | | 版本号 | | +| agentId | string | 必须 | | 代理商编号 | | +| sign | string | 必须 | | 验签值 | | +| reqSerialNo | string | 必须 | | 随机字符串,一般为一个和日期有关的随机数 | | +| data | object | 非必须 | | | | +| └merchantId | string | 必须 | | 商户号 | | +| └operation | string | 必须 | | 操作类型(query_by_order,query_by_transaction,query_by_third_order)) | | +| └orderId | string | 非必须 | | 订单编号 | | +| └transactionId | string | 非必须 | | 交易单号 | | +| └thirdOrderId | string | 非必须 | | 第三方订单号 | | + + +**返回参数:** + +| 名称 | 类型 | 是否必须 | 默认值 | 备注 | 其他信息 | +| --- | --- | --- | --- | --- | --- | +| traceId | string | 必须 | | 请求序列号 | | +| code | integer | 必须 | | 错误码 | | +| message | string | 必须 | | 错误信息 | | +| data | object | 非必须 | | | | +| └orderId | string | 非必须 | | 乐刷订单号 | | +| └errorCode | integer | 非必须 | | 请求错误码,成功为0 | | +| └errorMsg | string | 非必须 | | 错误提示信息,成功时值为空 | | +| └thirdOrderId | string | 非必须 | | 商户内部订单号 | | +| └merchantId | string | 非必须 | | 乐刷商户号 | | +| └amount | integer | 非必须 | | 交易金额 | | +| └orderStatus | integer | 非必须 | | 订单状态,0-初始状态(可能交易失败);5-交易成功;11-交易失败;53-交易失败(冲正成功);54-交易结果未知(冲正失败);3-退款成功;32-退款失败;33-退款状态未知;4-退货成功;41-退货失败;42-退货状态未知 | | +| └batchNo | string | 非必须 | | POS交易批次号 | | +| └flowNo | string | 非必须 | | POS交易流水号 | | +| └sign | string | 非必须 | | 签名 | | +| └merchantName | string | 非必须 | | 乐刷商户名称 | | +| transactionsInfo | object [] | 非必须 | | 流水 | item 类型: object | +| └merchantId | string | 非必须 | | 乐刷商户号 | | +| └pinpadUuid | string | 非必须 | | 乐刷SN | | +| └channelMerchantId | string | 非必须 | | 通道商户号 | | +| └channelTerminalId | string | 非必须 | | 通道终端号 | | +| └operationType | integer | 非必须 | | 操作类型,与订单状态一致 | | +| └inputType | string | 非必须 | | 交易用卡方式:051-插卡有密交易;052-插卡无密交易;071-挥卡有密交易;072-挥卡无密交易; | | +| └cardBank | string | 非必须 | | 发卡行名称 | | +| └authorizeCode | string | 非必须 | | 交易授权码 | | +| └referenceNo | string | 非必须 | | 交易参考号 | | +| └cardId | string | 非必须 | | 交易卡号 | | +| └pinpadSn | string | 非必须 | | 机具终端硬件号TUSN | | +| └time | string | 非必须 | | 交易时间 | | +| └cardType | integer | 非必须 | | 卡类型:0(借记卡),2(贷记卡) | | +| └channelBatchNo | string | 非必须 | | 通道交易批次号 | | +| └channelFlowNo | string | 非必须 | | 通道交易流水号 | | +| └transactionId | string | 非必须 | | 乐刷流水号 | | +| └channelId | string | 非必须 | | 通道id | | +| └channelMerchantName | string | 非必须 | | 通道商户名称 | | +| └amount | string | 非必须 | | 流水金额 | | + + +### 8、交易错误码信息参照表 + + +| **错误码** | **描述** | +| :---: | :---: | +| -20001 | 参数错误或无效 | +| -20002 | 无效的乐刷商户号 | +| -20003 | 商户订单号格式有误 | +| -20004 | 乐刷订单不存在 | +| -20005 | 服务错误 | +| -20006 | 验签失败 | +| -20007 | 签名 Key 未配置 | +| -20008 | 支付类型错误 | +| -20009 | 订单金额填写有误 | +| -20010 | 系统错误 | +| -20011 | 非法参数 | +| -20012 | 授权码为空 | +| -20013 | 非法的授权码 | +| -20014 | 未知类型的授权码 | +| -20015 | 非法的公众号支付标识 | +| -20016 | 无效乐刷订单号 | +| -20018 | 无效的第三方订单号或者乐刷订单号 | +| -20019 | 订单状态不允许退款 | +| -20020 | 订单未退款 | +| -20021 | 用户名跟商户ID不匹配 | +| -20022 | 无效的第三方退款订单号 | +| -20023 | 订单可退余额不足 | +| -20024 | 乐刷或第三方订单号必填 1 个 | +| -20025 | 乐刷或第三方退款订单号必填 1 个 | +| -20026 | 第三方订单号在该商户号下已经存在 | +| -20027 | 该订单已支付 | +| -20028 | 订单支付失败,不能撤销 | +| -20029 | 该订单已退款 | +| -20030 | 该订单退款失败,不能撤销 | +| -20031 | 无效的机具 SN 编号,请联系客服 | +| -20032 | 机具 SN 跟商户无绑定关系 | +| -4027 | 系统异常 | +| -4006 | 订单不存在 | +| -1006 | 该订单号已被用 | +| -1002 | 无效商户号 | +| -2034 | 测试商户号金额限制 | +| -4004 | 订单正在处理中 | +| -5103 | 商户退款 id 已存在 | +| -5014 | 商户无退款权限 | +| -5015 | 暂不支持退款 | +| -5016 | 订单状态异常 | +| -5017 | 订单可退金额不足 | +| -5018 | 订单退款次数超限 | +| -5019 | 订单未发起退款 | +| -5020 | 接口不支持该功能 | +| -2125 | 交易被风控拦截 | + + +| -5035 | 下单异常,可能原因有通道网络异常,通道状态异常,通道商户异常等 | +| :---: | :---: | + + +以实际返回的错误提示(error_msg)为准 diff --git a/docs/乐刷支付接入文档.md b/docs/乐刷支付接入文档.md index e174605..c5b4223 100644 --- a/docs/乐刷支付接入文档.md +++ b/docs/乐刷支付接入文档.md @@ -44,7 +44,8 @@ MD5 签名步骤: - 请求签名:一般不包含 `sign` 本身。 - 应答验签:按乐刷返回参数验签,实际返回字段可能因升级增加,验签时要允许新增字段。 -- 支付/退款通知验签:`error_code` 和 `sign` 不参与签名,空值参与签名,密钥使用乐刷提供的通知验签密钥。当前实现为 `LESHUA_NOTIFY_KEY`,为空时回退 `LESHUA_SIGN_KEY`。 +- 支付/退款通知验签:`error_code`、`leshua` 和 `sign` 不参与签名,其他返回字段按原样参与;空值参与签名;密钥只使用乐刷提供的通知验签密钥 `LESHUA_NOTIFY_KEY`。实测通知携带 `sign_type=MD5`,按普通返回字段参与签名。 +- 乐刷 XML 通知里的空标签也属于空值参数,必须保留并参与签名,例如 `` 应进入待签名串为 `goods_tag=`。 - `sign_type=SM3` 时签名结果为 64 位;不上传 `sign_type` 默认 MD5。 ## 统一下单 diff --git a/frontend/src/views/account/WalletView.vue b/frontend/src/views/account/WalletView.vue index e25a293..79135ab 100644 --- a/frontend/src/views/account/WalletView.vue +++ b/frontend/src/views/account/WalletView.vue @@ -1,5 +1,5 @@