本文档主要涉及开发指引中对停车离场场景的处理流程,希望通过对微信支付分停车服务扣款相关API的接入和开发流程的讲解,能帮助商户开发人员顺利完成对扣款受理、扣款结果等流程的接入和开发。

如下图所示,当用户停车离场时,商户方需要完成以下步骤的开发接入(下文中“服务”均代表微信支付分停车服务):

检查停车状态和服务状态

在进行离场操作时,商户后台系统首先需要检查用户车辆的停车状态和服务状态,若未通过检查则直接返回错误,避免执行后续的扣款流程。

parkingID := dao.GetParkingID(params.PlateNumber)
// 检查用户停车入场状态,若未停车则返回错误
if parkingID == "" {
    err := errors.Errorf("Vehicle %s is not parking now", params.PlateNumber)
    log.Print(err.Error())
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}
// 检查用户服务状态,若未开通服务则返回错误
if !dao.GetServiceStatus(params.PlateNumber) {
    err := errors.Errorf("Vehicle %s is not in credit parking service", params.PlateNumber)
    log.Print(err.Error())
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}

申请扣款

在调用扣费受理API后,微信支付分停车服务并不会同步进行扣款,而是先对商户和用户的基本信息、相关权限等进行校验,然后返回受理结果到商户后台系统。商户后台系统需要对受理结果进行相应的处理,比如在受理成功后放行车辆,受理失败则进行线下缴费的流程。

在调用扣费受理API申请扣款时,商户后台系统首先需要提供appid、商户订单号、订单优惠标记、回调通知URL等参数,并在收到扣费受理接口响应时判断HTTP状态码。若响应结果状态为成功,商户后台系统需要解析响应体并保存扣款记录;否则将相应的错误封装并返回给停车场端,提示扣款受理失败,需要执行现场缴费流程。

// 构造并发起申请扣款请求
req := model.CreateTransactionRequest{
    AppID:         os.Getenv("WXPAY_MERCHANT_APP_ID"),
    Description:   "停车场扣费",
    Attach:        "深圳分店",
    GoodsTag:      "WXG",
    ProfitSharing: "Y",
    OutTradeNo:    dao.GetOutParkingNo(params.PlateNumber),
    TradeScene:    "PARKING",
    NotifyURL:     fmt.Sprintf("%s/orders", os.Getenv("DOMAIN")),
    Amount: model.Amount{
        Total:    1,
        Currency: "CNY",
    },
    ParkingInfo: model.ParkingInfo{
        ParkingID:        parkingID,
        PlateNumber:      params.PlateNumber,
        PlateColor:       params.PlateColor,
        StartTime:        params.StartTime,
        EndTime:          params.StartTime.Add(time.Hour),
        ChargingDuration: 3600,
        ParkingName:      params.ParkingName,
        DeviceID:         util.GetRandIntStr(),
    },
}

reqBody, _ := json.Marshal(&req)
log.Printf("Req: %s", string(reqBody))
apiResult, err := client.GetClient().Post(
    r.Context(),
    "https://api.mch.weixin.qq.com/v3/vehicle/transactions/parking",
    reqBody,
)
if err != nil {
    err = errors.Errorf("Fail to create transaction: %v", err)
    log.Print(err.Error())
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

respBody, err := ioutil.ReadAll(apiResult.Response.Body)
if err != nil {
    err = errors.Errorf("Fail to read from response body: %v", err)
    log.Print(err.Error())
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}
log.Printf("Resp: %s", respBody)

resp := model.CreateTransactionResponse{}
if err = json.Unmarshal(respBody, &resp); err != nil {
    err = errors.Errorf("Fail to unmarshal from response body: %v", err)
    log.Print(err.Error())
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

// 记录用户扣款信息

更新停车状态

在扣款受理成功后,商户后台系统需要将用户车辆的停车状态置为“已离场”。

// 示例代码中通过删除车辆停车ID的方式实现离场操作,实际场景中商户需要根据自身情况进行用户停车状态的更新
dao.RemoveParkingID(params.PlateNumber)

处理订单支付结果回调

微信支付分停车服务在扣款受理成功后,将会异步执行扣款流程(若用户因余额不足等原因支付失败时,微信支付分停车服务提供垫资能力,保障扣款成功),并将实际扣款结果通过在申请扣款时商户提供的订单支付结果回调URL通知到商户方。

商户后台系统需要在订单状态通知中获取扣款状态并对分别处理(具体扣款状态和描述请参考订单支付结果通知文档)。正确处理回调之后,商户后台系统需要返回200或者204的HTTP状态码给微信支付分停车服务。

req := model.OrderStatusNotifyRequest{}
if _, err := client.GetNotifyHandler().ParseNotifyRequest(r.Context(), r, &req); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}
log.Printf("Req: %v", util.ToString(req))

switch req.TradeState {
case string("SUCCESS"):
    // 订单支付成功
case string("ACCEPTED"):
    // 订单已受理,后续一段时间内需要主动查询订单
    break
case string("PAY_FAIL"):
    // 订单支付失败,需要记录保存订单信息,并进行商户方支付失败场景的处理
    break
case string("REFUND"):
    // 订单转入退款,需要记录保存订单信息,并进行商户方退款场景的处理
    break
default:
    // 未知订单状态,需要记录保存订单信息
    break
}

util.WriteJSON(w, struct {
    Code string `json:"code"`
}{
    Code: "SUCCESS",
})

若在一段时间后(如24小时)仍未收到微信支付分停车服务的订单支付结果回调或回调结果为“已受理”时,商户后台系统应主动查单获取订单状态并记录相关信息,具体过程请参考查询订单开发指引。

扩展阅读



技术咨询

文档反馈