退款结果集统一处理

This commit is contained in:
egzosn
2020-08-16 22:51:15 +08:00
parent 9ebd5d1596
commit 545bc0c466
34 changed files with 2209 additions and 46 deletions

View File

@@ -236,7 +236,7 @@
RefundOrder order = new RefundOrder("支付宝单号", "我方系统单号", "退款金额", "订单总金额");
//非必填, 根据业务需求而定,可用于多次退款
order.setRefundNo("退款单号")
Map result = service.refund(order);
AliRefundResult result = service.refund(order);
```

View File

@@ -3,6 +3,7 @@ package com.egzosn.pay.ali.api;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.egzosn.pay.ali.bean.AliPayMessage;
import com.egzosn.pay.ali.bean.AliRefundResult;
import com.egzosn.pay.ali.bean.AliTransactionType;
import com.egzosn.pay.ali.bean.AliTransferType;
import com.egzosn.pay.ali.bean.OrderSettle;
@@ -437,7 +438,7 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
* @return 返回支付方申请退款后的结果
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public AliRefundResult refund(RefundOrder refundOrder) {
//获取公共参数
Map<String, Object> parameters = getPublicParameters(AliTransactionType.REFUND);
setAppAuthToken(parameters, refundOrder.getAttrs());
@@ -451,7 +452,9 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
//设置签名
setSign(parameters);
return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class);
final AliRefundResult refundResult = AliRefundResult.create(requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class));
refundResult.setOutRequestNo(refundOrder.getRefundNo());
return refundResult;
}

View File

@@ -0,0 +1,377 @@
package com.egzosn.pay.ali.bean;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.egzosn.pay.common.bean.BaseRefundResult;
import com.egzosn.pay.common.bean.CurType;
import com.egzosn.pay.common.bean.DefaultCurType;
import com.egzosn.pay.common.bean.RefundOrder;
/**
* 支付宝退款结果返回
*
* @author Egan
* <pre>
* email egzosn@gmail.com
* date 2020/8/16 17:53
* </pre>
*/
public class AliRefundResult extends BaseRefundResult {
/**
* 网关返回码,详见文档 40004
*/
private String code;
/**
* 网关返回码描述,详见文档 Business Failed
*/
private String msg;
/**
* 业务返回码参见具体的API接口文档 ACQ.TRADE_HAS_SUCCESS
*/
@JSONField(name = "sub_code")
private String subCode;
/**
* 业务返回码描述参见具体的API接口文档 交易已被支付
*/
@JSONField(name = "sub_msg")
private String subMsg;
/**
* 签名,详见文档
*/
private String sign;
/**
* 支付宝交易号
*/
@JSONField(name = "trade_no")
private String tradeNo;
/**
* 商户订单号 6823789339978248
*/
@JSONField(name = "out_trade_no")
private String outTradeNo;
/**
* 标识一次退款请求,同一笔交易多次退款需要保证唯一
* <p>
* 因支付宝退款结果中没有返回此参数,该参数从{@link RefundOrder#getRefundNo()}获取
*/
private String outRequestNo;
/**
* 用户的登录id 159****5620
*/
@JSONField(name = "buyer_logon_id")
private String buyerLogonId;
/**
* 本次退款是否发生了资金变化 Y
*/
@JSONField(name = "fund_change")
private String fundChange;
/**
* 退款总金额 88.88
*/
@JSONField(name = "refund_fee")
private BigDecimal refundFee;
/**
* 退款币种信息 USD
*/
@JSONField(name = "refund_currency")
private DefaultCurType refundCurrency;
/**
* 退款支付时间 2014-11-27 15:45:57
*/
@JSONField(name = "gmt_refund_pay")
private Date gmtRefundPay;
/**
* 退款使用的资金渠道。
* 只有在签约中指定需要返回资金明细或者入参的query_options中指定时才返回该字段信息。
*/
@JSONField(name = "refund_detail_item_list")
private TradeFundBill refundDetailItemList;
/**
* 交易在支付时候的门店名称
*/
@JSONField(name = "store_name")
private String storeName;
/**
* 买家在支付宝的用户id 2088101117955611
*/
@JSONField(name = "buyer_user_id")
private String buyerUserId;
/**
* 退回的前置资产列表
*/
@JSONField(name = "refund_preset_paytool_list")
private PresetPayToolInfo refundPresetPaytoolList;
/**
* 退款清算编号,用于清算对账使用;
* 只在银行间联交易场景下返回该信息; 2018101610032004620239146945
*/
@JSONField(name = "refund_settlement_id")
private String refundSettlementId;
/**
* 本次退款金额中买家退款金额 88.88
*/
@JSONField(name = "present_refund_buyer_amount")
private String presentRefundBuyerAmount;
/**
* 本次退款金额中平台优惠退款金额 88.88
*/
@JSONField(name = "present_refund_discount_amount")
private String presentRefundDiscountAmount;
/**
* 本次退款金额中商家优惠退款金额 88.88
*/
@JSONField(name = "present_refund_mdiscount_amount")
private String presentRefundMdiscountAmount;
/**
* 获取退款请求结果状态码
*
* @return 状态码
*/
@Override
public String getCode() {
return code;
}
/**
* 获取退款请求结果状态提示信息
*
* @return 提示信息
*/
@Override
public String getMsg() {
return msg;
}
/**
* 返回业务结果状态码
*
* @return 业务结果状态码
*/
@Override
public String getResultCode() {
return subCode;
}
/**
* 返回业务结果状态提示信息
*
* @return 业务结果状态提示信息
*/
@Override
public String getResultMsg() {
return subMsg;
}
/**
* 退款金额
*
* @return 退款金额
*/
@Override
public BigDecimal getRefundFee() {
return refundFee;
}
/**
* 退款币种信息
*
* @return 币种信息
*/
@Override
public CurType getRefundCurrency() {
return refundCurrency;
}
/**
* 支付平台交易号
* 发起支付时 支付平台(如支付宝)返回的交易订单号
*
* @return 支付平台交易号
*/
@Override
public String getTradeNo() {
return tradeNo;
}
/**
* 支付订单号
* 发起支付时,用户系统的订单号
*
* @return 支付订单号
*/
@Override
public String getOutTradeNo() {
return outTradeNo;
}
/**
* 商户退款单号
*
* @return 商户退款单号
*/
@Override
public String getRefundNo() {
return outRequestNo;
}
public void setCode(String code) {
this.code = code;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getSubCode() {
return subCode;
}
public void setSubCode(String subCode) {
this.subCode = subCode;
}
public String getSubMsg() {
return subMsg;
}
public void setSubMsg(String subMsg) {
this.subMsg = subMsg;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public void setTradeNo(String tradeNo) {
this.tradeNo = tradeNo;
}
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public String getOutRequestNo() {
return outRequestNo;
}
public void setOutRequestNo(String outRequestNo) {
this.outRequestNo = outRequestNo;
}
public String getBuyerLogonId() {
return buyerLogonId;
}
public void setBuyerLogonId(String buyerLogonId) {
this.buyerLogonId = buyerLogonId;
}
public String getFundChange() {
return fundChange;
}
public void setFundChange(String fundChange) {
this.fundChange = fundChange;
}
public void setRefundFee(BigDecimal refundFee) {
this.refundFee = refundFee;
}
public void setRefundCurrency(DefaultCurType refundCurrency) {
this.refundCurrency = refundCurrency;
}
public Date getGmtRefundPay() {
return gmtRefundPay;
}
public void setGmtRefundPay(Date gmtRefundPay) {
this.gmtRefundPay = gmtRefundPay;
}
public TradeFundBill getRefundDetailItemList() {
return refundDetailItemList;
}
public void setRefundDetailItemList(TradeFundBill refundDetailItemList) {
this.refundDetailItemList = refundDetailItemList;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public String getBuyerUserId() {
return buyerUserId;
}
public void setBuyerUserId(String buyerUserId) {
this.buyerUserId = buyerUserId;
}
public PresetPayToolInfo getRefundPresetPaytoolList() {
return refundPresetPaytoolList;
}
public void setRefundPresetPaytoolList(PresetPayToolInfo refundPresetPaytoolList) {
this.refundPresetPaytoolList = refundPresetPaytoolList;
}
public String getRefundSettlementId() {
return refundSettlementId;
}
public void setRefundSettlementId(String refundSettlementId) {
this.refundSettlementId = refundSettlementId;
}
public String getPresentRefundBuyerAmount() {
return presentRefundBuyerAmount;
}
public void setPresentRefundBuyerAmount(String presentRefundBuyerAmount) {
this.presentRefundBuyerAmount = presentRefundBuyerAmount;
}
public String getPresentRefundDiscountAmount() {
return presentRefundDiscountAmount;
}
public void setPresentRefundDiscountAmount(String presentRefundDiscountAmount) {
this.presentRefundDiscountAmount = presentRefundDiscountAmount;
}
public String getPresentRefundMdiscountAmount() {
return presentRefundMdiscountAmount;
}
public void setPresentRefundMdiscountAmount(String presentRefundMdiscountAmount) {
this.presentRefundMdiscountAmount = presentRefundMdiscountAmount;
}
public static final AliRefundResult create(Map<String, Object> result){
AliRefundResult refundResult = new JSONObject(result).toJavaObject(AliRefundResult.class);
refundResult.setAttrs(result);
return refundResult;
}
}

View File

@@ -0,0 +1,44 @@
package com.egzosn.pay.ali.bean;
import java.math.BigDecimal;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 退回的前置资产列表
*
* @author Egan
* <pre>
* email egzosn@gmail.com
* date 2020/8/16 18:58
* </pre>
*/
public class PresetPayToolInfo {
/**
* 必填 32前置资产金额 12.21
*/
private BigDecimal[] amount;
/**
* 前置资产类型编码和收单支付传入的preset_pay_tool里面的类型编码保持一致。盒马礼品卡:HEMA抓猫猫红包:T_CAT_COUPON
*/
@JSONField(name = "assert_type_code")
private String assertTypeCode;
public BigDecimal[] getAmount() {
return amount;
}
public void setAmount(BigDecimal[] amount) {
this.amount = amount;
}
public String getAssertTypeCode() {
return assertTypeCode;
}
public void setAssertTypeCode(String assertTypeCode) {
this.assertTypeCode = assertTypeCode;
}
}

View File

@@ -0,0 +1,82 @@
package com.egzosn.pay.ali.bean;
import java.math.BigDecimal;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 退款使用的资金渠道。
* 只有在签约中指定需要返回资金明细或者入参的query_options中指定时才返回该字段信息。
* @author Egan
* <pre>
* email egzosn@gmail.com
* date 2020/8/16 18:51
* </pre>
*/
public class TradeFundBill {
/**
* 交易使用的资金渠道,详见 支付渠道列表 ALIPAYACCOUNT
*/
@JSONField(name = "fund_channel")
private String fundChannel;
/**
* 银行卡支付时的银行代码 CEB
*/
@JSONField(name = "bank_code")
private String bankCode;
/**
* 该支付工具类型所使用的金额 10
*/
private BigDecimal amount;
/**
* 渠道实际付款金额 11.21
*/
@JSONField(name = "real_amount")
private BigDecimal realAmount;
/**
* 渠道所使用的资金类型,目前只在资金渠道(fund_channel)是银行卡渠道(BANKCARD)的情况下才返回该信息(DEBIT_CARD:借记卡,CREDIT_CARD:信用卡,MIXED_CARD:借贷合一卡) DEBIT_CARD
*/
@JSONField(name = "fund_type")
private String fundType;
public String getFundChannel() {
return fundChannel;
}
public void setFundChannel(String fundChannel) {
this.fundChannel = fundChannel;
}
public String getBankCode() {
return bankCode;
}
public void setBankCode(String bankCode) {
this.bankCode = bankCode;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public BigDecimal getRealAmount() {
return realAmount;
}
public void setRealAmount(BigDecimal realAmount) {
this.realAmount = realAmount;
}
public String getFundType() {
return fundType;
}
public void setFundType(String fundType) {
this.fundType = fundType;
}
}

View File

@@ -316,7 +316,7 @@ public class BaiduPayService extends BasePayService<BaiduPayConfigStorage> {
* @return 退款结果
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public BaseRefundResult refund(RefundOrder refundOrder) {
Map<String, Object> parameters = getUseQueryPay();
BaiduTransactionType transactionType = BaiduTransactionType.APPLY_REFUND;
parameters.put(METHOD, transactionType.getMethod());
@@ -329,7 +329,53 @@ public class BaiduPayService extends BasePayService<BaiduPayConfigStorage> {
parameters.put("bizRefundBatchId", refundOrder.getRefundNo());
parameters.put(APP_KEY, payConfigStorage.getAppKey());
parameters.put(RSA_SIGN, getRsaSign(parameters, RSA_SIGN));
return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class);
final JSONObject result = requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class);
return new BaseRefundResult(result) {
@Override
public String getCode() {
return getAttrString(RESPONSE_STATUS);
}
@Override
public String getMsg() {
return null;
}
@Override
public String getResultCode() {
return null;
}
@Override
public String getResultMsg() {
return null;
}
@Override
public BigDecimal getRefundFee() {
return null;
}
@Override
public CurType getRefundCurrency() {
return null;
}
@Override
public String getTradeNo() {
return null;
}
@Override
public String getOutTradeNo() {
return null;
}
@Override
public String getRefundNo() {
return null;
}
};
}

View File

@@ -263,7 +263,7 @@ public abstract class BasePayService<PC extends PayConfigStorage> implements Pay
@Override
public <T> T refund(RefundOrder refundOrder, Callback<T> callback) {
return callback.perform(refund(refundOrder));
return callback.perform(refund(refundOrder).getAttrs());
}

View File

@@ -252,7 +252,7 @@ public interface PayService<PC extends PayConfigStorage> {
* @param refundOrder 退款订单信息
* @return 返回支付方申请退款后的结果
*/
Map<String, Object> refund(RefundOrder refundOrder);
RefundResult refund(RefundOrder refundOrder);
/**
* 申请退款接口

View File

@@ -0,0 +1,337 @@
package com.egzosn.pay.common.bean;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* 基础的退款结果对象
*
* @author Egan
* <pre>
* email egzosn@gmail.com
* date 2020/8/16 19:29
* </pre>
*/
public abstract class BaseRefundResult implements RefundResult {
/**
* 属性集,支付宝退款结果
*/
private Map<String, Object> attrs;
public BaseRefundResult() {
}
public BaseRefundResult(Map<String, Object> attrs) {
this.attrs = attrs;
}
/**
* 获取退款结果原信息集
*
* @return 属性
*/
@Override
public Map<String, Object> getAttrs() {
return attrs;
}
public void setAttrs(Map<String, Object> attrs) {
this.attrs = attrs;
}
/**
* 获取退款结果属性值
*
* @param key 属性名
* @return 属性
*/
@Override
public Object getAttr(String key) {
return attrs.get(key);
}
/**
* 获取退款结果属性值
*
* @param key 属性名
* @return 属性值
*/
@Override
public String getAttrString(String key) {
return attrs.get(key).toString();
}
/**
* 获取退款结果属性值
*
* @param key 属性名
* @return 属性值
*/
@Override
public BigDecimal getAttrDecimal(String key) {
return new BigDecimal(getAttrString(key));
}
/**
* Returns the number of key-value mappings in this map. If the
* map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*
* @return the number of key-value mappings in this map
*/
@Override
public int size() {
return attrs.size();
}
/**
* Returns <tt>true</tt> if this map contains no key-value mappings.
*
* @return <tt>true</tt> if this map contains no key-value mappings
*/
@Override
public boolean isEmpty() {
return attrs.isEmpty();
}
/**
* Returns <tt>true</tt> if this map contains a mapping for the specified
* key. More formally, returns <tt>true</tt> if and only if
* this map contains a mapping for a key <tt>k</tt> such that
* <tt>(key==null ? k==null : key.equals(k))</tt>. (There can be
* at most one such mapping.)
*
* @param key key whose presence in this map is to be tested
* @return <tt>true</tt> if this map contains a mapping for the specified
* key
* @throws ClassCastException if the key is of an inappropriate type for
* this map
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key is null and this map
* does not permit null keys
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
*/
@Override
public boolean containsKey(Object key) {
return attrs.containsKey(key);
}
/**
* Returns <tt>true</tt> if this map maps one or more keys to the
* specified value. More formally, returns <tt>true</tt> if and only if
* this map contains at least one mapping to a value <tt>v</tt> such that
* <tt>(value==null ? v==null : value.equals(v))</tt>. This operation
* will probably require time linear in the map size for most
* implementations of the <tt>Map</tt> interface.
*
* @param value value whose presence in this map is to be tested
* @return <tt>true</tt> if this map maps one or more keys to the
* specified value
* @throws ClassCastException if the value is of an inappropriate type for
* this map
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified value is null and this
* map does not permit null values
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
*/
@Override
public boolean containsValue(Object value) {
return attrs.containsValue(value);
}
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null :
* key.equals(k))}, then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
*
* <p>If this map permits null values, then a return value of
* {@code null} does not <i>necessarily</i> indicate that the map
* contains no mapping for the key; it's also possible that the map
* explicitly maps the key to {@code null}. The {@link #containsKey
* containsKey} operation may be used to distinguish these two cases.
*
* @param key the key whose associated value is to be returned
* @return the value to which the specified key is mapped, or
* {@code null} if this map contains no mapping for the key
* @throws ClassCastException if the key is of an inappropriate type for
* this map
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key is null and this map
* does not permit null keys
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
*/
@Override
public Object get(Object key) {
return attrs.get(key);
}
/**
* Associates the specified value with the specified key in this map
* (optional operation). If the map previously contained a mapping for
* the key, the old value is replaced by the specified value. (A map
* <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
* if {@link #containsKey(Object) m.containsKey(k)} would return
* <tt>true</tt>.)
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>,
* if the implementation supports <tt>null</tt> values.)
* @throws UnsupportedOperationException if the <tt>put</tt> operation
* is not supported by this map
* @throws ClassCastException if the class of the specified key or value
* prevents it from being stored in this map
* @throws NullPointerException if the specified key or value is null
* and this map does not permit null keys or values
* @throws IllegalArgumentException if some property of the specified key
* or value prevents it from being stored in this map
*/
@Override
public Object put(String key, Object value) {
return attrs.put(key, value);
}
/**
* Removes the mapping for a key from this map if it is present
* (optional operation). More formally, if this map contains a mapping
* from key <tt>k</tt> to value <tt>v</tt> such that
* <code>(key==null ? k==null : key.equals(k))</code>, that mapping
* is removed. (The map can contain at most one such mapping.)
*
* <p>Returns the value to which this map previously associated the key,
* or <tt>null</tt> if the map contained no mapping for the key.
*
* <p>If this map permits null values, then a return value of
* <tt>null</tt> does not <i>necessarily</i> indicate that the map
* contained no mapping for the key; it's also possible that the map
* explicitly mapped the key to <tt>null</tt>.
*
* <p>The map will not contain a mapping for the specified key once the
* call returns.
*
* @param key key whose mapping is to be removed from the map
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this map
* @throws ClassCastException if the key is of an inappropriate type for
* this map
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key is null and this
* map does not permit null keys
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
*/
@Override
public Object remove(Object key) {
return attrs.remove(key);
}
/**
* Copies all of the mappings from the specified map to this map
* (optional operation). The effect of this call is equivalent to that
* of calling {@link #put(Object, Object) put(k, v)} on this map once
* for each mapping from key <tt>k</tt> to value <tt>v</tt> in the
* specified map. The behavior of this operation is undefined if the
* specified map is modified while the operation is in progress.
*
* @param m mappings to be stored in this map
* @throws UnsupportedOperationException if the <tt>putAll</tt> operation
* is not supported by this map
* @throws ClassCastException if the class of a key or value in the
* specified map prevents it from being stored in this map
* @throws NullPointerException if the specified map is null, or if
* this map does not permit null keys or values, and the
* specified map contains null keys or values
* @throws IllegalArgumentException if some property of a key or value in
* the specified map prevents it from being stored in this map
*/
@Override
public void putAll(Map<? extends String, ?> m) {
attrs.putAll(m);
}
/**
* Removes all of the mappings from this map (optional operation).
* The map will be empty after this call returns.
*
* @throws UnsupportedOperationException if the <tt>clear</tt> operation
* is not supported by this map
*/
@Override
public void clear() {
attrs.clear();
}
/**
* Returns a {@link Set} view of the keys contained in this map.
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
* the iterator's own <tt>remove</tt> operation), the results of
* the iteration are undefined. The set supports element removal,
* which removes the corresponding mapping from the map, via the
* <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
* operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
* operations.
*
* @return a set view of the keys contained in this map
*/
@Override
public Set<String> keySet() {
return attrs.keySet();
}
/**
* Returns a {@link Collection} view of the values contained in this map.
* The collection is backed by the map, so changes to the map are
* reflected in the collection, and vice-versa. If the map is
* modified while an iteration over the collection is in progress
* (except through the iterator's own <tt>remove</tt> operation),
* the results of the iteration are undefined. The collection
* supports element removal, which removes the corresponding
* mapping from the map, via the <tt>Iterator.remove</tt>,
* <tt>Collection.remove</tt>, <tt>removeAll</tt>,
* <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
* support the <tt>add</tt> or <tt>addAll</tt> operations.
*
* @return a collection view of the values contained in this map
*/
@Override
public Collection<Object> values() {
return attrs.values();
}
/**
* Returns a {@link Set} view of the mappings contained in this map.
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
* the iterator's own <tt>remove</tt> operation, or through the
* <tt>setValue</tt> operation on a map entry returned by the
* iterator) the results of the iteration are undefined. The set
* supports element removal, which removes the corresponding
* mapping from the map, via the <tt>Iterator.remove</tt>,
* <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
* <tt>clear</tt> operations. It does not support the
* <tt>add</tt> or <tt>addAll</tt> operations.
*
* @return a set view of the mappings contained in this map
*/
@Override
public Set<Entry<String, Object>> entrySet() {
return attrs.entrySet();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original egan.
* Copyright 2017 the original egan.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,6 @@
package com.egzosn.pay.common.bean;
import java.io.Serializable;
import java.util.Map;
/**
@@ -11,7 +12,7 @@ import java.util.Map;
* date 2020/01/05 13:34
* </pre>
*/
public interface Order {
public interface Order extends Serializable {
/**
* 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。

View File

@@ -0,0 +1,103 @@
package com.egzosn.pay.common.bean;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Map;
/**
* 退款结果
*
* 这里继承Map为兼容方案后续版本进行删除
* @author Egan
* <pre>
* email egzosn@gmail.com
* date 2020/8/16 9:55
* </pre>
*/
public interface RefundResult extends Map<String, Object>, Serializable {
/**
* 获取退款结果原信息集
*
* @return 属性
*/
Map<String, Object> getAttrs();
/**
* 获取退款结果属性值
*
* @param key 属性名
* @return 属性值
*/
Object getAttr(String key);
/**
* 获取退款结果属性值
*
* @param key 属性名
* @return 属性值
*/
String getAttrString(String key);
/**
* 获取退款结果属性值
*
* @param key 属性名
* @return 属性值
*/
BigDecimal getAttrDecimal(String key);
/**
* 获取退款请求结果状态码
* @return 状态码
*/
String getCode();
/**
* 获取退款请求结果状态提示信息
* @return 提示信息
*/
String getMsg();
/**
* 返回业务结果状态码
* @return 业务结果状态码
*/
String getResultCode();
/**
* 返回业务结果状态提示信息
* @return 业务结果状态提示信息
*/
String getResultMsg();
/**
* 退款金额
* @return 退款金额
*/
BigDecimal getRefundFee();
/**
* 退款币种信息
* @return 币种信息
*/
CurType getRefundCurrency();
/**
* 支付平台交易号
* 发起支付时 支付平台(如支付宝)返回的交易订单号
*
* @return 支付平台交易号
*/
String getTradeNo();
/**
* 支付订单号
* 发起支付时,用户系统的订单号
* @return 支付订单号
*/
String getOutTradeNo();
/**
* 商户退款单号
* @return 商户退款单号
*/
String getRefundNo();
}

View File

@@ -352,9 +352,4 @@ public class XML {
}
public static void main(String[] args) {
String text = "<datas><data><code>0</code><users><user><id>1</id><name>张三</name></user><user><id>2</id><name>张4</name></user></users></data></datas>";
System.out.println( getMap2Xml(toJSONObject(text), "datas", "utf-8"));
}
}

View File

@@ -86,7 +86,7 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.1</version>
<version>2.11.2</version>
</dependency>
</dependencies>

View File

@@ -4,6 +4,7 @@ package com.egzosn.pay.demo.controller;
import com.egzosn.pay.ali.api.AliPayConfigStorage;
import com.egzosn.pay.ali.api.AliPayService;
import com.egzosn.pay.ali.bean.AliRefundResult;
import com.egzosn.pay.ali.bean.AliTransactionType;
import com.egzosn.pay.ali.bean.AliTransferOrder;
import com.egzosn.pay.ali.bean.AliTransferType;
@@ -279,7 +280,7 @@ public class AliPayController {
* @return 返回支付方申请退款后的结果
*/
@RequestMapping("refund")
public Map<String, Object> refund(RefundOrder order) {
public AliRefundResult refund(RefundOrder order) {
return service.refund(order);
}

View File

@@ -422,7 +422,7 @@ public class PayController {
* @return 返回支付方申请退款后的结果
*/
@RequestMapping("refund")
public Map<String, Object> refund(Integer payId, RefundOrder order) {
public RefundResult refund(Integer payId, RefundOrder order) {
PayResponse payResponse = service.getPayResponse(payId);
// return payResponse.getService().refund(order.getTradeNo(), order.getOutTradeNo(), order.getRefundAmount(), order.getTotalAmount());

View File

@@ -5,6 +5,7 @@ import com.egzosn.pay.common.api.PayService;
import com.egzosn.pay.common.bean.DefaultCurType;
import com.egzosn.pay.common.bean.PayOrder;
import com.egzosn.pay.common.bean.RefundOrder;
import com.egzosn.pay.common.bean.RefundResult;
import com.egzosn.pay.common.http.HttpConfigStorage;
import com.egzosn.pay.paypal.api.PayPalConfigStorage;
import com.egzosn.pay.paypal.api.PayPalPayService;
@@ -86,7 +87,7 @@ public class PayPalPayController {
* @return 返回支付方申请退款后的结果
*/
@RequestMapping("refund")
public Map<String, Object> refund() {
public RefundResult refund() {
// TODO 这里需要 refundAmount curType description tradeNo
RefundOrder order = new RefundOrder();
order.setCurType(DefaultCurType.USD);

View File

@@ -12,6 +12,7 @@ import com.egzosn.pay.common.util.sign.SignUtils;
import com.egzosn.pay.demo.request.QueryOrder;
import com.egzosn.pay.union.api.UnionPayConfigStorage;
import com.egzosn.pay.union.api.UnionPayService;
import com.egzosn.pay.union.bean.UnionRefundResult;
import com.egzosn.pay.union.bean.UnionTransactionType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -266,7 +267,7 @@ public class UnionPayController {
* @return 返回支付方申请退款后的结果
*/
@RequestMapping("refund")
public Map<String, Object> refund(RefundOrder order) {
public UnionRefundResult refund(RefundOrder order) {
return service.refund(order);
}

View File

@@ -309,7 +309,7 @@ public class WxPayController {
* @return 返回支付方申请退款后的结果
*/
@RequestMapping("refund")
public Map<String, Object> refund(RefundOrder order) {
public WxRefundResult refund(RefundOrder order) {
if("ssl 退款证书".equals(KEYSTORE)){
throw new RuntimeException("请设置好SSL退款证书");
}

View File

@@ -9,6 +9,7 @@ import com.egzosn.pay.common.util.DateUtils;
import com.egzosn.pay.common.util.Util;
import com.egzosn.pay.common.util.sign.SignUtils;
import com.egzosn.pay.common.util.str.StringUtils;
import com.egzosn.pay.fuiou.bean.FuiouRefundResult;
import com.egzosn.pay.fuiou.bean.FuiouTransactionType;
import java.awt.image.BufferedImage;
@@ -379,7 +380,7 @@ public class FuiouPayService extends BasePayService<FuiouPayConfigStorage> {
* @return 退款返回结果集
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public RefundResult refund(RefundOrder refundOrder) {
Map<String, Object> params = new HashMap<>();
//商户代码
params.put("mchnt_cd", payConfigStorage.getPid());
@@ -394,7 +395,7 @@ public class FuiouPayService extends BasePayService<FuiouPayConfigStorage> {
params.putAll(refundOrder.getAttrs());
params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset()));
JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpRefundGate, params, JSONObject.class);
return resultJson;
return FuiouRefundResult.create(resultJson);
}

View File

@@ -0,0 +1,166 @@
package com.egzosn.pay.fuiou.bean;
import java.math.BigDecimal;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.egzosn.pay.common.bean.BaseRefundResult;
import com.egzosn.pay.common.bean.CurType;
/**
* 富友退款结果
* @author Egan、
* <pre>
* email egzosn@gmail.com
* date 2020/8/16 21:17
* </pre>
*/
public class FuiouRefundResult extends BaseRefundResult {
private String originOrderId;
/**
* 退款状态
* 0 已受理
* 1 成功
* 2 失败
*/
@JSONField(name = "order_st")
private String orderSt;
/**
* 错误代码
* 5341表示退款成功
*/
@JSONField(name = "order_pay_code")
private String orderPayCode;
/**
* 错误中文描述
* 5341表示退款成功
*/
@JSONField(name = "order_pay_error")
private String orderPayError;
/**
* 富友流水号
* 供商户查询支付交易状态及对账用
*/
@JSONField(name = "fy_ssn")
private String fySsn;
/**
* 保留字段
*/
private String resv1;
/**
* MD5摘要数据
* mchnt_cd + "|" + order_st + "|" +
* order_pay_code + "|" +
* order_pay_error + "|" + fy_ssn + "|" +
* resv1 + "|" + mchnt_key
* 做MD5摘要
* 其中mchnt_key 为32位的商户密钥系统分配
*/
private String md5;
/**
* 退款金额
*/
private BigDecimal refundAmt;
/**
* 获取退款请求结果状态码
*
* @return 状态码
*/
@Override
public String getCode() {
return orderSt;
}
/**
* 获取退款请求结果状态提示信息
*
* @return 提示信息
*/
@Override
public String getMsg() {
return orderPayError;
}
/**
* 返回业务结果状态码
*
* @return 业务结果状态码
*/
@Override
public String getResultCode() {
return orderPayCode;
}
/**
* 返回业务结果状态提示信息
*
* @return 业务结果状态提示信息
*/
@Override
public String getResultMsg() {
return orderPayError;
}
/**
* 退款金额
*
* @return 退款金额
*/
@Override
public BigDecimal getRefundFee() {
return null;
}
/**
* 退款币种信息
*
* @return 币种信息
*/
@Override
public CurType getRefundCurrency() {
return null;
}
/**
* 支付平台交易号
* 发起支付时 支付平台(如支付宝)返回的交易订单号
*
* @return 支付平台交易号
*/
@Override
public String getTradeNo() {
return fySsn;
}
/**
* 支付订单号
* 发起支付时,用户系统的订单号
*
* @return 支付订单号
*/
@Override
public String getOutTradeNo() {
return originOrderId;
}
/**
* 商户退款单号
*
* @return 商户退款单号
*/
@Override
public String getRefundNo() {
return null;
}
public static final FuiouRefundResult create(Map<String, Object> result){
FuiouRefundResult refundResult = new JSONObject(result).toJavaObject(FuiouRefundResult.class);
refundResult.setAttrs(result);
return refundResult;
}
}

View File

@@ -120,7 +120,7 @@
//Map result = service.refund(null, "我方系统单号", null, null);
//支付宝单号与我方系统单号二选一
RefundOrder order = new RefundOrder(null, "我方系统单号", null, null);
Map result = service.refund(order);
RefundResult result = service.refund(order);
```

View File

@@ -18,6 +18,7 @@ import org.apache.http.Header;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHeader;
import java.math.BigDecimal;
import java.util.*;
/**
@@ -323,8 +324,53 @@ public class PayoneerPayService extends BasePayService<PayoneerConfigStorage> im
* @return 返回支付方申请退款后的结果
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
return close(refundOrder.getTradeNo(), refundOrder.getOutTradeNo());
public RefundResult refund(RefundOrder refundOrder) {
return new BaseRefundResult(close(refundOrder.getTradeNo(), refundOrder.getOutTradeNo())) {
@Override
public String getCode() {
return getAttrString(CODE);
}
@Override
public String getMsg() {
return null;
}
@Override
public String getResultCode() {
return null;
}
@Override
public String getResultMsg() {
return null;
}
@Override
public BigDecimal getRefundFee() {
return null;
}
@Override
public CurType getRefundCurrency() {
return null;
}
@Override
public String getTradeNo() {
return null;
}
@Override
public String getOutTradeNo() {
return null;
}
@Override
public String getRefundNo() {
return null;
}
};
}

View File

@@ -95,6 +95,6 @@
order.setDescription(" description ");
order.setTradeNo("paypal 平台的单号");
order.setRefundAmount(new BigDecimal(0.01));
Map result = service.refund(order);
RefundResult result = service.refund(order);
```

View File

@@ -249,7 +249,7 @@ public class PayPalPayService extends BasePayService<PayPalConfigStorage>{
* @return 返回支付方申请退款后的结果
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public RefundResult refund(RefundOrder refundOrder) {
JSONObject request = new JSONObject();
if (null != refundOrder.getRefundAmount() && BigDecimal.ZERO.compareTo( refundOrder.getRefundAmount()) == -1){
@@ -267,7 +267,52 @@ public class PayPalPayService extends BasePayService<PayPalConfigStorage>{
HttpStringEntity httpEntity = new HttpStringEntity(request.toJSONString(), ContentType.APPLICATION_JSON);
httpEntity.setHeaders(authHeader());
JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, refundOrder.getTradeNo());
return resp;
return new BaseRefundResult(resp) {
@Override
public String getCode() {
return getAttrString("state");
}
@Override
public String getMsg() {
return null;
}
@Override
public String getResultCode() {
return null;
}
@Override
public String getResultMsg() {
return null;
}
@Override
public BigDecimal getRefundFee() {
return null;
}
@Override
public CurType getRefundCurrency() {
return null;
}
@Override
public String getTradeNo() {
return null;
}
@Override
public String getOutTradeNo() {
return null;
}
@Override
public String getRefundNo() {
return null;
}
};
}
/**

View File

@@ -190,6 +190,6 @@
RefundOrder order = new RefundOrder(null, "原交易查询流水号", "退款金额", "订单总金额");
order.setRefundNo("退款单号")
Map result = service.refund(order);
UnionRefundResult result = service.refund(order);
```

View File

@@ -17,6 +17,7 @@ import com.egzosn.pay.common.util.sign.encrypt.RSA2;
import com.egzosn.pay.common.util.str.StringUtils;
import com.egzosn.pay.union.bean.SDKConstants;
import com.egzosn.pay.union.bean.UnionPayMessage;
import com.egzosn.pay.union.bean.UnionRefundResult;
import com.egzosn.pay.union.bean.UnionTransactionType;
import java.io.ByteArrayInputStream;
@@ -591,7 +592,7 @@ public class UnionPayService extends BasePayService<UnionPayConfigStorage> {
* @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO
* @return 返回支付方申请退款后的结果
*/
public Map<String, Object> unionRefundOrConsumeUndo(RefundOrder refundOrder, UnionTransactionType type) {
public UnionRefundResult unionRefundOrConsumeUndo(RefundOrder refundOrder, UnionTransactionType type) {
Map<String, Object> params = this.getCommonParam();
type.convertMap(params);
params.put(SDKConstants.param_orderId, refundOrder.getRefundNo());
@@ -601,12 +602,11 @@ public class UnionPayService extends BasePayService<UnionPayConfigStorage> {
this.setSign(params);
String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class);
JSONObject response = UriVariables.getParametersToMap(responseStr);
if (this.verify(response)) {
if (SDKConstants.OK_RESP_CODE.equals(response.getString(SDKConstants.param_respCode))) {
// String origRespCode = response.getString(SDKConstants.param_origRespCode);
//交易成功,更新商户订单状态
//TODO
return response;
final UnionRefundResult refundResult = UnionRefundResult.create(response);
if (SDKConstants.OK_RESP_CODE.equals(refundResult.getRespCode())) {
return refundResult;
}
throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString()));
@@ -628,7 +628,7 @@ public class UnionPayService extends BasePayService<UnionPayConfigStorage> {
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public UnionRefundResult refund(RefundOrder refundOrder) {
return unionRefundOrConsumeUndo(refundOrder, UnionTransactionType.REFUND);
}

View File

@@ -0,0 +1,326 @@
package com.egzosn.pay.union.bean;
import java.math.BigDecimal;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.egzosn.pay.common.bean.BaseRefundResult;
import com.egzosn.pay.common.bean.CurType;
/**
* 银联退款结果
*
* @author Egan
* email egzosn@gmail.com
* date 2020/8/16 22:15
*/
public class UnionRefundResult extends BaseRefundResult {
/**
* 二维码数据
*/
private String qrCode;
/**
* 签名
*/
private String signature;
/**
* 签名方法
*/
private String signMethod;
/**
* 应答码
*/
private String respCode;
/**
* 应答信息
*/
private String respMsg;
/**
* 签名公钥证书
*/
private String signPubKeyCert;
/**
* 版本号
*/
private String version;
/**
* 编码方式
*/
private String encoding;
/**
* 产品类型
*/
private String bizType;
/**
* 订单发送时间
*/
private String txnTime;
/**
* 交易类型
*/
private String txnType;
/**
* 交易子类
*/
private String txnSubType;
/**
* 接入类型
* 0商户直连接入
* 1收单机构接入
* 2平台商户接入
*/
private String accessType;
/**
* 请求方保留域
*/
private String reqReserved;
/**
* 商户代码
*/
private String merId;
/**
* 商户订单号
*/
private String orderId;
/**
* 保留域
*/
private String reserved;
/**
* 获取退款请求结果状态码
*
* @return 状态码
*/
@Override
public String getCode() {
return respCode;
}
/**
* 获取退款请求结果状态提示信息
*
* @return 提示信息
*/
@Override
public String getMsg() {
return respMsg;
}
/**
* 返回业务结果状态码
*
* @return 业务结果状态码
*/
@Override
public String getResultCode() {
return null;
}
/**
* 返回业务结果状态提示信息
*
* @return 业务结果状态提示信息
*/
@Override
public String getResultMsg() {
return null;
}
/**
* 退款金额
*
* @return 退款金额
*/
@Override
public BigDecimal getRefundFee() {
return null;
}
/**
* 退款币种信息
*
* @return 币种信息
*/
@Override
public CurType getRefundCurrency() {
return null;
}
/**
* 支付平台交易号
* 发起支付时 支付平台(如支付宝)返回的交易订单号
*
* @return 支付平台交易号
*/
@Override
public String getTradeNo() {
return null;
}
/**
* 支付订单号
* 发起支付时,用户系统的订单号
*
* @return 支付订单号
*/
@Override
public String getOutTradeNo() {
return orderId;
}
/**
* 商户退款单号
*
* @return 商户退款单号
*/
@Override
public String getRefundNo() {
return null;
}
public String getQrCode() {
return qrCode;
}
public void setQrCode(String qrCode) {
this.qrCode = qrCode;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
public String getSignMethod() {
return signMethod;
}
public void setSignMethod(String signMethod) {
this.signMethod = signMethod;
}
public String getRespCode() {
return respCode;
}
public void setRespCode(String respCode) {
this.respCode = respCode;
}
public String getRespMsg() {
return respMsg;
}
public void setRespMsg(String respMsg) {
this.respMsg = respMsg;
}
public String getSignPubKeyCert() {
return signPubKeyCert;
}
public void setSignPubKeyCert(String signPubKeyCert) {
this.signPubKeyCert = signPubKeyCert;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getEncoding() {
return encoding;
}
public void setEncoding(String encoding) {
this.encoding = encoding;
}
public String getBizType() {
return bizType;
}
public void setBizType(String bizType) {
this.bizType = bizType;
}
public String getTxnTime() {
return txnTime;
}
public void setTxnTime(String txnTime) {
this.txnTime = txnTime;
}
public String getTxnType() {
return txnType;
}
public void setTxnType(String txnType) {
this.txnType = txnType;
}
public String getTxnSubType() {
return txnSubType;
}
public void setTxnSubType(String txnSubType) {
this.txnSubType = txnSubType;
}
public String getAccessType() {
return accessType;
}
public void setAccessType(String accessType) {
this.accessType = accessType;
}
public String getReqReserved() {
return reqReserved;
}
public void setReqReserved(String reqReserved) {
this.reqReserved = reqReserved;
}
public String getMerId() {
return merId;
}
public void setMerId(String merId) {
this.merId = merId;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getReserved() {
return reserved;
}
public void setReserved(String reserved) {
this.reserved = reserved;
}
public static final UnionRefundResult create(Map<String, Object> result){
UnionRefundResult refundResult = new JSONObject(result).toJavaObject(UnionRefundResult.class);
refundResult.setAttrs(result);
return refundResult;
}
}

View File

@@ -393,7 +393,7 @@ public class WxYouDianPayService extends BasePayService<WxYouDianPayConfigStorag
* @return 返回支付方申请退款后的结果
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public RefundResult refund(RefundOrder refundOrder) {
String apbNonce = SignUtils.randomStr();
TreeMap<String, String> data = new TreeMap<>();
data.put("access_token", payConfigStorage.getAccessToken());
@@ -408,8 +408,53 @@ public class WxYouDianPayService extends BasePayService<WxYouDianPayConfigStorag
data.put("refund_fee", refundOrder.getRefundAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toString());
String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset());
String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign;
JSONObject jsonObject = execute(getReqUrl(YoudianTransactionType.NATIVE_STATUS) + "?" + queryParam, MethodType.GET, null);
return jsonObject;
JSONObject jsonObject = execute(getReqUrl(YoudianTransactionType.REFUND) + "?" + queryParam, MethodType.GET, null);
return new BaseRefundResult() {
@Override
public String getCode() {
return getAttrString("errorcode");
}
@Override
public String getMsg() {
return getAttrString("msg");
}
@Override
public String getResultCode() {
return null;
}
@Override
public String getResultMsg() {
return null;
}
@Override
public BigDecimal getRefundFee() {
return null;
}
@Override
public CurType getRefundCurrency() {
return null;
}
@Override
public String getTradeNo() {
return null;
}
@Override
public String getOutTradeNo() {
return null;
}
@Override
public String getRefundNo() {
return null;
}
};
}

View File

@@ -215,7 +215,7 @@
RefundOrder order = new RefundOrder("微信单号", "我方系统单号", "退款金额", "订单总金额");
//可用于多次退款
order.setRefundNo("退款单号")
Map result = service.refund(order);
WxRefundResult result = service.refund(order);
```

View File

@@ -488,7 +488,7 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> implements
* @return 返回支付方申请退款后的结果
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public WxRefundResult refund(RefundOrder refundOrder) {
//获取公共参数
Map<String, Object> parameters = getPublicParameters();
@@ -506,7 +506,7 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> implements
parameters.putAll(refundOrder.getAttrs());
//设置签名
setSign(parameters);
return requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class);
return WxRefundResult.create(requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class));
}

View File

@@ -0,0 +1,496 @@
package com.egzosn.pay.wx.bean;
import java.math.BigDecimal;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.egzosn.pay.common.bean.BaseRefundResult;
import com.egzosn.pay.common.bean.CurType;
/**
* 微信退款结果
* @author Egan
* <pre></pre>
* email egzosn@gmail.com
* date 2020/8/16 21:29
*/
public class WxRefundResult extends BaseRefundResult {
/**
* 返回状态码
* SUCCESS/FAIL
* 此字段是通信标识,表示接口层的请求结果,并非退款状态。
*/
@JSONField(name = "return_code")
private String returnCode;
/**
* 返回信息
* 当return_code为FAIL时返回信息为错误原因 ,例如
* 签名失败
* 参数格式校验错误
*/
@JSONField(name = "return_msg")
private String returnMsg;
/**
* 业务结果
* SUCCESS/FAIL
* SUCCESS退款申请接收成功结果通过退款查询接口查询
* FAIL 提交业务失败
*/
@JSONField(name = "result_code")
private String resultCode;
/**
* 错误代码
* 名称 描述 原因 解决方案
* SYSTEMERROR 接口返回错误 系统超时等 请不要更换商户退款单号请使用相同参数再次调用API。
* BIZERR_NEED_RETRY 退款业务流程错误,需要商户触发重试来解决 并发情况下,业务被拒绝,商户重试即可解决 请不要更换商户退款单号请使用相同参数再次调用API。
* TRADE_OVERDUE 订单已经超过退款期限 订单已经超过可退款的最大期限(支付后一年内可退款) 请选择其他方式自行退款
* ERROR 业务错误 申请退款业务发生错误 该错误都会返回具体的错误原因,请根据实际返回做相应处理。
* USER_ACCOUNT_ABNORMAL 退款请求失败 用户帐号注销 此状态代表退款申请失败,商户可自行处理退款。
* INVALID_REQ_TOO_MUCH 无效请求过多 连续错误请求数过多被系统短暂屏蔽 请检查业务是否正常确认业务正常后请在1分钟后再来重试
* NOTENOUGH 余额不足 商户可用退款余额不足 此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。
* INVALID_TRANSACTIONID 无效transaction_id 请求参数未按指引进行填写 请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败
* PARAM_ERROR 参数错误 请求参数未按指引进行填写 请求参数错误,请重新检查再调用退款申请
* APPID_NOT_EXIST APPID不存在 参数中缺少APPID 请检查APPID是否正确
* MCHID_NOT_EXIST MCHID不存在 参数中缺少MCHID 请检查MCHID是否正确
* ORDERNOTEXIST 订单号不存在 缺少有效的订单号 请检查你的订单号是否正确且是否已支付,未支付的订单不能发起退款
* REQUIRE_POST_METHOD 请使用post方法 未使用post传递参数 请检查请求参数是否通过post方法提交
* SIGNERROR 签名错误 参数签名结果不正确 请检查签名参数和方法是否都符合签名算法要求
* XML_FORMAT_ERROR XML格式错误 XML格式错误 请检查XML参数格式是否正确
* FREQUENCY_LIMITED 频率限制 2个月之前的订单申请退款有频率限制 该笔退款未受理,请降低频率后重试
* NOAUTH 异常IP请求不予受理 请求ip异常 如果是动态ip请登录商户平台后台关闭ip安全配置
* 如果是静态ip请确认商户平台配置的请求ip 在不在配的ip列表里
* CERT_ERROR 证书校验错误 请检查证书是否正确,证书是否过期或作废。 请检查证书是否正确,证书是否过期或作废。
* REFUND_FEE_MISMATCH 订单金额或退款金额与之前请求不一致,请核实后再试 订单金额或退款金额与之前请求不一致,请核实后再试 订单金额或退款金额与之前请求不一致,请核实后再试
* INVALID_REQUEST 请求参数符合参数格式,但不符合业务规则 此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。 此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。
*/
@JSONField(name = "err_code")
private String errCode;
/**
* 错误代码描述
*/
@JSONField(name = "err_code_des")
private String errCodeDes;
/**
* 应用id
* 公众账号ID
* 微信分配的公众账号ID
*/
@JSONField(name = "appid")
private String appid;
/**
* 商户号
* 微信支付分配的商户号
*/
@JSONField(name = "mch_id")
private String mchId;
/**
* 随机字符串不长于32位
*/
@JSONField(name = "nonce_str")
private String nonceStr;
/**
* 签名
*/
@JSONField(name = "sign")
private String sign;
/**
* 微信订单号
*/
@JSONField(name = "transaction_id")
private String transactionId;
/**
* 商户订单号
*/
@JSONField(name = "out_trade_no")
private String outTradeNo;
/**
* 商户退款单号
*/
@JSONField(name = "out_refund_no")
private String outRefundNo;
/**
* 微信退款单号
*/
@JSONField(name = "refund_id")
private String refundId;
/**
* 退款金额
* 退款总金额,单位为分,可以做部分退款
*/
@JSONField(name = "refund_fee")
private BigDecimal refundFee;
/**
* 应结退款金额
* 去掉非充值代金券退款金额后的退款金额,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
*/
@JSONField(name = "settlement_refund_fee")
private BigDecimal settlementRefundFee;
/**
* 标价金额
* 订单总金额,单位为分,只能为整数,详见支付金额
*/
@JSONField(name = "total_fee")
private BigDecimal totalFee;
/**
* 应结订单金额
* 去掉非充值代金券金额后的订单总金额,应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
*/
@JSONField(name = "settlement_total_fee")
private BigDecimal settlementTotalFee;
/**
* 标价币种
* 订单金额货币类型符合ISO 4217标准的三位字母代码默认人民币CNY其他值列表详见货币类型
*/
@JSONField(name = "fee_type")
private CurType feeType;
/**
* 现金支付金额
* 现金支付金额,单位为分,只能为整数,详见支付金额
*/
@JSONField(name = "cash_fee")
private BigDecimal cashFee;
/**
* 现金支付币种
* 货币类型符合ISO 4217标准的三位字母代码默认人民币CNY其他值列表详见货币类型
*/
@JSONField(name = "cash_fee_type")
private CurType cashFeeType;
/**
* 现金退款金额
* 现金退款金额,单位为分,只能为整数,详见支付金额
*/
@JSONField(name = "cash_refund_fee")
private BigDecimal cashRefundFee;
/**
* 代金券类型
* CASH--充值代金券
*
* NO_CASH---非充值代金券
*
* 订单使用代金券时有返回取值CASH、NO_CASH。$n为下标,从0开始编号举例coupon_type_0
* 这里只接收0的其余请自行获取
*/
@JSONField(name = "coupon_type_0")
private String couponType0;
/**
* 代金券退款总金额
* 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
*/
@JSONField(name = "coupon_refund_fee")
private BigDecimal couponRefundFee;
/**
* 单个代金券退款金额
* 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
* 这里只接收0的其余请自行获取
*/
@JSONField(name = "coupon_refund_fee_0")
private BigDecimal couponRefundFee0;
@JSONField(name = "coupon_refund_count")
/**
* 退款代金券使用数量
* 退款代金券使用数量
*/
private Integer couponRefundCount;
/**
* 退款代金券ID, $n为下标从0开始编号
* 这里只接收0的其余请自行获取
*/
@JSONField(name = "coupon_refund_id_0")
private Integer couponRefundId0;
/**
* 获取退款请求结果状态码
*
* @return 状态码
*/
@Override
public String getCode() {
return returnCode;
}
/**
* 获取退款请求结果状态提示信息
*
* @return 提示信息
*/
@Override
public String getMsg() {
return returnMsg;
}
/**
* 返回业务结果状态码
*
* @return 业务结果状态码
*/
@Override
public String getResultCode() {
return resultCode;
}
/**
* 返回业务结果状态提示信息
*
* @return 业务结果状态提示信息
*/
@Override
public String getResultMsg() {
return errCodeDes;
}
/**
* 退款金额
*
* @return 退款金额
*/
@Override
public BigDecimal getRefundFee() {
return refundFee;
}
/**
* 退款币种信息
*
* @return 币种信息
*/
@Override
public CurType getRefundCurrency() {
return feeType;
}
/**
* 支付平台交易号
* 发起支付时 支付平台(如支付宝)返回的交易订单号
*
* @return 支付平台交易号
*/
@Override
public String getTradeNo() {
return transactionId;
}
/**
* 支付订单号
* 发起支付时,用户系统的订单号
*
* @return 支付订单号
*/
@Override
public String getOutTradeNo() {
return outTradeNo;
}
/**
* 商户退款单号
*
* @return 商户退款单号
*/
@Override
public String getRefundNo() {
return outRefundNo;
}
public String getReturnCode() {
return returnCode;
}
public void setReturnCode(String returnCode) {
this.returnCode = returnCode;
}
public String getReturnMsg() {
return returnMsg;
}
public void setReturnMsg(String returnMsg) {
this.returnMsg = returnMsg;
}
public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}
public String getErrCode() {
return errCode;
}
public void setErrCode(String errCode) {
this.errCode = errCode;
}
public String getErrCodeDes() {
return errCodeDes;
}
public void setErrCodeDes(String errCodeDes) {
this.errCodeDes = errCodeDes;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMchId() {
return mchId;
}
public void setMchId(String mchId) {
this.mchId = mchId;
}
public String getNonceStr() {
return nonceStr;
}
public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public String getOutRefundNo() {
return outRefundNo;
}
public void setOutRefundNo(String outRefundNo) {
this.outRefundNo = outRefundNo;
}
public String getRefundId() {
return refundId;
}
public void setRefundId(String refundId) {
this.refundId = refundId;
}
public void setRefundFee(BigDecimal refundFee) {
this.refundFee = refundFee;
}
public BigDecimal getSettlementRefundFee() {
return settlementRefundFee;
}
public void setSettlementRefundFee(BigDecimal settlementRefundFee) {
this.settlementRefundFee = settlementRefundFee;
}
public BigDecimal getTotalFee() {
return totalFee;
}
public void setTotalFee(BigDecimal totalFee) {
this.totalFee = totalFee;
}
public BigDecimal getSettlementTotalFee() {
return settlementTotalFee;
}
public void setSettlementTotalFee(BigDecimal settlementTotalFee) {
this.settlementTotalFee = settlementTotalFee;
}
public CurType getFeeType() {
return feeType;
}
public void setFeeType(CurType feeType) {
this.feeType = feeType;
}
public BigDecimal getCashFee() {
return cashFee;
}
public void setCashFee(BigDecimal cashFee) {
this.cashFee = cashFee;
}
public CurType getCashFeeType() {
return cashFeeType;
}
public void setCashFeeType(CurType cashFeeType) {
this.cashFeeType = cashFeeType;
}
public BigDecimal getCashRefundFee() {
return cashRefundFee;
}
public void setCashRefundFee(BigDecimal cashRefundFee) {
this.cashRefundFee = cashRefundFee;
}
public String getCouponType0() {
return couponType0;
}
public void setCouponType0(String couponType0) {
this.couponType0 = couponType0;
}
public BigDecimal getCouponRefundFee() {
return couponRefundFee;
}
public void setCouponRefundFee(BigDecimal couponRefundFee) {
this.couponRefundFee = couponRefundFee;
}
public BigDecimal getCouponRefundFee0() {
return couponRefundFee0;
}
public void setCouponRefundFee0(BigDecimal couponRefundFee0) {
this.couponRefundFee0 = couponRefundFee0;
}
public Integer getCouponRefundCount() {
return couponRefundCount;
}
public void setCouponRefundCount(Integer couponRefundCount) {
this.couponRefundCount = couponRefundCount;
}
public Integer getCouponRefundId0() {
return couponRefundId0;
}
public void setCouponRefundId0(Integer couponRefundId0) {
this.couponRefundId0 = couponRefundId0;
}
public static final WxRefundResult create(Map<String, Object> result){
WxRefundResult refundResult = new JSONObject(result).toJavaObject(WxRefundResult.class);
refundResult.setAttrs(result);
return refundResult;
}
}

View File

@@ -11,6 +11,7 @@ import com.egzosn.pay.common.util.sign.SignUtils;
import com.egzosn.pay.common.util.str.StringUtils;
import com.egzosn.pay.yiji.bean.YiJiTransactionType;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
@@ -300,7 +301,7 @@ public class YiJiPayService extends BasePayService<YiJiPayConfigStorage> {
* @return 返回支付方申请退款后的结果
*/
@Override
public Map<String, Object> refund(RefundOrder refundOrder) {
public RefundResult refund(RefundOrder refundOrder) {
Map<String, Object> orderInfo = getPublicParameters(YiJiTransactionType.tradeRefund);
orderInfo.put("orderNo", refundOrder.getOutTradeNo());
orderInfo.put("outOrderNo", refundOrder.getOutTradeNo());
@@ -308,7 +309,52 @@ public class YiJiPayService extends BasePayService<YiJiPayConfigStorage> {
orderInfo.put("refundTime", DateUtils.formatDay(refundOrder.getOrderDate()));
orderInfo.put("refundReason", refundOrder.getDescription());
setSign(orderInfo);
return getHttpRequestTemplate().postForObject(getReqUrl(YiJiTransactionType.tradeRefund), orderInfo, JSONObject.class);
return new BaseRefundResult(getHttpRequestTemplate().postForObject(getReqUrl(YiJiTransactionType.tradeRefund), orderInfo, JSONObject.class)) {
@Override
public String getCode() {
return null;
}
@Override
public String getMsg() {
return null;
}
@Override
public String getResultCode() {
return null;
}
@Override
public String getResultMsg() {
return null;
}
@Override
public BigDecimal getRefundFee() {
return null;
}
@Override
public CurType getRefundCurrency() {
return null;
}
@Override
public String getTradeNo() {
return null;
}
@Override
public String getOutTradeNo() {
return null;
}
@Override
public String getRefundNo() {
return null;
}
};
}
/**

View File

@@ -67,7 +67,7 @@
<pay.version>2.13.3-SNAPSHOT</pay.version>
<httpmime.version>4.5.4</httpmime.version>
<log4j.version>1.2.17</log4j.version>
<fastjson.version>1.2.58</fastjson.version>
<fastjson.version>1.2.73</fastjson.version>
<zxing.version>3.3.1</zxing.version>
<junit.version>5.5.1</junit.version>
</properties>
@@ -92,10 +92,11 @@
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
<version>1.2.73</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>