|
|
@@ -8,6 +8,8 @@ import okhttp3.*;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import shop.alien.entity.result.R;
|
|
|
+import shop.alien.entity.store.RefundRecord;
|
|
|
+import shop.alien.store.service.RefundRecordService;
|
|
|
import shop.alien.store.strategy.payment.PaymentStrategy;
|
|
|
import shop.alien.store.util.WXPayUtility;
|
|
|
import shop.alien.util.common.UniqueRandomNumGenerator;
|
|
|
@@ -22,7 +24,12 @@ import java.nio.charset.StandardCharsets;
|
|
|
import java.security.PrivateKey;
|
|
|
import java.security.PublicKey;
|
|
|
import java.security.Signature;
|
|
|
+import java.text.ParseException;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.time.OffsetDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
import java.util.Base64;
|
|
|
+import java.util.Date;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
@@ -39,6 +46,8 @@ import java.util.Map;
|
|
|
@RequiredArgsConstructor
|
|
|
public class WeChatPaymentStrategyImpl implements PaymentStrategy {
|
|
|
|
|
|
+ private final RefundRecordService refundRecordService;
|
|
|
+
|
|
|
@Value("${payment.wechatPay.host}")
|
|
|
private String wechatPayApiHost;
|
|
|
|
|
|
@@ -202,7 +211,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
|
|
|
result.put("prepayId", response.prepayId);
|
|
|
result.put("appId", appId);
|
|
|
result.put("mchId", mchId);
|
|
|
- result.put("outTradeNo", request.outTradeNo);
|
|
|
+ result.put("orderNo", request.outTradeNo);
|
|
|
// 生成sign
|
|
|
// appId
|
|
|
|
|
|
@@ -251,6 +260,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
|
|
|
|
|
|
@Override
|
|
|
public String handleRefund(Map<String,String> params) throws Exception {
|
|
|
+
|
|
|
CreateRequest request = new CreateRequest();
|
|
|
// 微信支付订单号和商户订单号必须二选一,不能同时为空,查询的时候使用的是商户订单号
|
|
|
//request.transactionId = "1217752501201407033233368018";
|
|
|
@@ -265,7 +275,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
|
|
|
//request.fundsAccount = ReqFundsAccount.AVAILABLE;
|
|
|
// 金额信息
|
|
|
request.amount = new AmountReq();
|
|
|
- request.amount.refund = new BigDecimal(params.get("refundAmount")).multiply(new BigDecimal(100)).longValue();
|
|
|
+ request.amount.refund = new BigDecimal(params.get("refundAmount")).longValue();
|
|
|
// 退款出资账户及金额 目前不需要,需要的时候再看
|
|
|
/*
|
|
|
request.amount.from = new ArrayList<>();
|
|
|
@@ -276,7 +286,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
|
|
|
request.amount.from.add(fromItem);
|
|
|
};*/
|
|
|
// 订单总金额
|
|
|
- request.amount.total = new BigDecimal(params.get("totalAmount")).multiply(new BigDecimal(100)).longValue();
|
|
|
+ request.amount.total = new BigDecimal(params.get("totalAmount")).longValue();
|
|
|
// 退款币种 目前默认人民币 CNY
|
|
|
request.amount.currency = "CNY";
|
|
|
// 退款商品信息 目前不需要,需要的时候再看
|
|
|
@@ -292,16 +302,119 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
|
|
|
goodsDetailItem.refundQuantity = 1L;
|
|
|
request.goodsDetail.add(goodsDetailItem);
|
|
|
};*/
|
|
|
+ // 记录退款请求信息
|
|
|
+ log.info("开始处理微信支付退款,商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分,退款原因:{}",
|
|
|
+ request.outTradeNo, request.outRefundNo, request.amount.refund, request.amount.total, request.reason);
|
|
|
+ String refundResult = "";
|
|
|
try {
|
|
|
Refund response = refundRun(request);
|
|
|
- String refundReslut = "";
|
|
|
- // TODO: 请求成功,继续业务逻辑
|
|
|
- System.out.println(response);
|
|
|
- } catch (WXPayUtility.ApiException e) {
|
|
|
- // TODO: 请求失败,根据状态码执行不同的逻辑
|
|
|
- e.printStackTrace();
|
|
|
+ // 退款状态
|
|
|
+ String status = response.status != null ? response.status.name() : "UNKNOWN";
|
|
|
+ if ("SUCCESS".equals(status) || "PROCESSING".equals(status)) {
|
|
|
+ // refund_id 申请退款受理成功时,该笔退款单在微信支付侧生成的唯一标识。
|
|
|
+ String refundId = response.refundId;
|
|
|
+ // 商户申请退款时传的商户系统内部退款单号。
|
|
|
+ String outRefundNo = response.outRefundNo != null ? response.outRefundNo : request.outRefundNo;
|
|
|
+ // 微信支付订单号
|
|
|
+ String transactionId = response.transactionId;
|
|
|
+
|
|
|
+ // 退款金额信息
|
|
|
+ String refundAmount = response.amount != null && response.amount.refund != null
|
|
|
+ ? String.valueOf(response.amount.refund) : String.valueOf(request.amount.refund);
|
|
|
+ String totalAmount = response.amount != null && response.amount.total != null
|
|
|
+ ? String.valueOf(response.amount.total) : String.valueOf(request.amount.total);
|
|
|
+ // 退款成功时间
|
|
|
+ String successTime = response.successTime;
|
|
|
+ // 退款创建时间
|
|
|
+ String createTime = response.createTime;
|
|
|
+ // 退款渠道
|
|
|
+ String channel = response.channel != null ? response.channel.name() : "UNKNOWN";
|
|
|
+
|
|
|
+ // 记录退款成功详细信息
|
|
|
+ log.info("微信支付退款成功 - 商户订单号:{},微信支付订单号:{},商户退款单号:{},微信退款单号:{}," +
|
|
|
+ "退款状态:{},退款金额:{}分,订单总金额:{}分,退款渠道:{},创建时间:{},成功时间:{}",
|
|
|
+ request.outTradeNo, transactionId, outRefundNo, refundId, status, refundAmount,
|
|
|
+ totalAmount, channel, createTime, successTime != null ? successTime : "未完成");
|
|
|
+
|
|
|
+ // 保存到通用退款记录表
|
|
|
+ try {
|
|
|
+ RefundRecord refundRecord = buildRefundRecordFromWeChatResponse(response, request, params);
|
|
|
+ if (refundRecord != null) {
|
|
|
+ // 检查是否已存在,避免重复插入
|
|
|
+ long count = refundRecordService.lambdaQuery()
|
|
|
+ .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
|
|
|
+ .count();
|
|
|
+ if (count == 0) {
|
|
|
+ refundRecordService.save(refundRecord);
|
|
|
+ log.info("微信支付退款记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ } else {
|
|
|
+ log.info("微信支付退款记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("保存微信支付退款记录到RefundRecord表失败", e);
|
|
|
+ // 不抛出异常,避免影响原有逻辑
|
|
|
+ }
|
|
|
+
|
|
|
+ refundResult = "调用成功";
|
|
|
+ return refundResult;
|
|
|
+ } else {
|
|
|
+ log.error("微信支付退款失败 - 商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分," +
|
|
|
+ "退款状态:{}",
|
|
|
+ request.outTradeNo, request.outRefundNo, request.amount.refund,
|
|
|
+ request.amount.total, status);
|
|
|
+
|
|
|
+ // 保存失败记录到通用退款记录表
|
|
|
+ try {
|
|
|
+ RefundRecord refundRecord = buildRefundRecordFromWeChatError(response, request, params, status);
|
|
|
+ if (refundRecord != null) {
|
|
|
+ // 检查是否已存在,避免重复插入
|
|
|
+ long count = refundRecordService.lambdaQuery()
|
|
|
+ .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
|
|
|
+ .count();
|
|
|
+ if (count == 0) {
|
|
|
+ refundRecordService.save(refundRecord);
|
|
|
+ log.info("微信支付退款失败记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ } else {
|
|
|
+ log.info("微信支付退款失败记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("保存微信支付退款失败记录到RefundRecord表失败", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return "退款失败";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ // 记录其他异常
|
|
|
+ log.error("微信支付退款异常 - 商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分," +
|
|
|
+ "退款原因:{},异常信息:{}",
|
|
|
+ request.outTradeNo, request.outRefundNo, request.amount.refund,
|
|
|
+ request.amount.total, request.reason, e.getMessage(), e);
|
|
|
+
|
|
|
+ // 保存异常记录到通用退款记录表
|
|
|
+ try {
|
|
|
+ RefundRecord refundRecord = buildRefundRecordFromWeChatException(request, params, e);
|
|
|
+ if (refundRecord != null) {
|
|
|
+ // 检查是否已存在,避免重复插入
|
|
|
+ long count = refundRecordService.lambdaQuery()
|
|
|
+ .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
|
|
|
+ .count();
|
|
|
+ if (count == 0) {
|
|
|
+ refundRecordService.save(refundRecord);
|
|
|
+ log.info("微信支付退款异常记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ } else {
|
|
|
+ log.info("微信支付退款异常记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.error("保存微信支付退款异常记录到RefundRecord表失败", ex);
|
|
|
+ }
|
|
|
+
|
|
|
+ refundResult = "退款处理异常:" + e.getMessage();
|
|
|
+ return refundResult;
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -864,4 +977,265 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
|
|
|
@SerializedName("UNAVAILABLE")
|
|
|
UNAVAILABLE
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从微信支付退款响应构建RefundRecord对象(成功情况)
|
|
|
+ */
|
|
|
+ private RefundRecord buildRefundRecordFromWeChatResponse(Refund response, CreateRequest request, Map<String, String> params) {
|
|
|
+ try {
|
|
|
+ RefundRecord record = new RefundRecord();
|
|
|
+
|
|
|
+ // 基本信息
|
|
|
+ record.setPayType(PaymentEnum.WECHAT_PAY.getType());
|
|
|
+ record.setOutTradeNo(response.outTradeNo != null ? response.outTradeNo : request.outTradeNo);
|
|
|
+ record.setTransactionId(response.transactionId);
|
|
|
+ record.setOutRefundNo(response.outRefundNo != null ? response.outRefundNo : request.outRefundNo);
|
|
|
+ record.setRefundId(response.refundId);
|
|
|
+ record.setRefundStatus(response.status != null ? response.status.name() : "UNKNOWN");
|
|
|
+
|
|
|
+ // 金额信息(微信返回的是分)
|
|
|
+ if (response.amount != null) {
|
|
|
+ record.setTotalAmount(response.amount.total);
|
|
|
+ record.setRefundAmount(response.amount.refund);
|
|
|
+ record.setActualRefundAmount(response.amount.refund); // 微信退款金额就是实际退款金额
|
|
|
+ if (response.amount.currency != null) {
|
|
|
+ record.setCurrency(response.amount.currency);
|
|
|
+ } else {
|
|
|
+ record.setCurrency("CNY");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ record.setTotalAmount(request.amount.total);
|
|
|
+ record.setRefundAmount(request.amount.refund);
|
|
|
+ record.setActualRefundAmount(request.amount.refund);
|
|
|
+ record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 退款原因
|
|
|
+ record.setRefundReason(request.reason);
|
|
|
+
|
|
|
+ // 退款渠道
|
|
|
+ if (response.channel != null) {
|
|
|
+ record.setRefundChannel(response.channel.name());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 退款资金来源
|
|
|
+ if (response.fundsAccount != null) {
|
|
|
+ record.setFundsAccount(response.fundsAccount.name());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 用户收到退款账户
|
|
|
+ record.setUserReceivedAccount(response.userReceivedAccount);
|
|
|
+
|
|
|
+ // 退款详情(如果有优惠信息)
|
|
|
+ if (response.promotionDetail != null && !response.promotionDetail.isEmpty()) {
|
|
|
+ record.setRefundDetail(WXPayUtility.toJson(response.promotionDetail));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 退款时间(微信返回的是ISO 8601格式字符串,需要转换为Date)
|
|
|
+ if (response.createTime != null) {
|
|
|
+ Date createTime = parseWeChatTime(response.createTime);
|
|
|
+ if (createTime != null) {
|
|
|
+ record.setRefundCreateTime(createTime);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (response.successTime != null) {
|
|
|
+ Date successTime = parseWeChatTime(response.successTime);
|
|
|
+ if (successTime != null) {
|
|
|
+ record.setRefundSuccessTime(successTime);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 业务信息(从params获取,如果有的话)
|
|
|
+ if (params != null) {
|
|
|
+ if (params.containsKey("userId")) {
|
|
|
+ try {
|
|
|
+ record.setUserId(Integer.parseInt(params.get("userId")));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("解析userId失败: {}", params.get("userId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (params.containsKey("orderId")) {
|
|
|
+ record.setOrderId(params.get("orderId"));
|
|
|
+ }
|
|
|
+ if (params.containsKey("storeId")) {
|
|
|
+ try {
|
|
|
+ record.setStoreId(Integer.parseInt(params.get("storeId")));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("解析storeId失败: {}", params.get("storeId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 响应数据
|
|
|
+ record.setResponseData(WXPayUtility.toJson(response));
|
|
|
+
|
|
|
+ // 标准字段
|
|
|
+ record.setDeleteFlag(0);
|
|
|
+ record.setCreatedTime(new Date());
|
|
|
+
|
|
|
+ return record;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("构建RefundRecord对象失败", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从微信支付退款错误响应构建RefundRecord对象(失败情况)
|
|
|
+ */
|
|
|
+ private RefundRecord buildRefundRecordFromWeChatError(Refund response, CreateRequest request, Map<String, String> params, String status) {
|
|
|
+ try {
|
|
|
+ RefundRecord record = new RefundRecord();
|
|
|
+
|
|
|
+ // 基本信息
|
|
|
+ record.setPayType(PaymentEnum.WECHAT_PAY.getType());
|
|
|
+ record.setOutTradeNo(response.outTradeNo != null ? response.outTradeNo : request.outTradeNo);
|
|
|
+ record.setTransactionId(response.transactionId);
|
|
|
+ record.setOutRefundNo(response.outRefundNo != null ? response.outRefundNo : request.outRefundNo);
|
|
|
+ record.setRefundId(response.refundId);
|
|
|
+ record.setRefundStatus(status);
|
|
|
+
|
|
|
+ // 金额信息
|
|
|
+ if (response.amount != null) {
|
|
|
+ record.setTotalAmount(response.amount.total);
|
|
|
+ record.setRefundAmount(response.amount.refund);
|
|
|
+ record.setCurrency(response.amount.currency != null ? response.amount.currency : "CNY");
|
|
|
+ } else {
|
|
|
+ record.setTotalAmount(request.amount.total);
|
|
|
+ record.setRefundAmount(request.amount.refund);
|
|
|
+ record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 退款原因
|
|
|
+ record.setRefundReason(request.reason);
|
|
|
+
|
|
|
+ // 退款时间
|
|
|
+ if (response.createTime != null) {
|
|
|
+ Date createTime = parseWeChatTime(response.createTime);
|
|
|
+ if (createTime != null) {
|
|
|
+ record.setRefundCreateTime(createTime);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 业务信息
|
|
|
+ if (params != null) {
|
|
|
+ if (params.containsKey("userId")) {
|
|
|
+ try {
|
|
|
+ record.setUserId(Integer.parseInt(params.get("userId")));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("解析userId失败: {}", params.get("userId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (params.containsKey("orderId")) {
|
|
|
+ record.setOrderId(params.get("orderId"));
|
|
|
+ }
|
|
|
+ if (params.containsKey("storeId")) {
|
|
|
+ try {
|
|
|
+ record.setStoreId(Integer.parseInt(params.get("storeId")));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("解析storeId失败: {}", params.get("storeId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 响应数据
|
|
|
+ record.setResponseData(WXPayUtility.toJson(response));
|
|
|
+
|
|
|
+ // 标准字段
|
|
|
+ record.setDeleteFlag(0);
|
|
|
+ record.setCreatedTime(new Date());
|
|
|
+
|
|
|
+ return record;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("构建RefundRecord对象失败", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从微信支付退款异常构建RefundRecord对象(异常情况)
|
|
|
+ */
|
|
|
+ private RefundRecord buildRefundRecordFromWeChatException(CreateRequest request, Map<String, String> params, Exception e) {
|
|
|
+ try {
|
|
|
+ RefundRecord record = new RefundRecord();
|
|
|
+
|
|
|
+ // 基本信息
|
|
|
+ record.setPayType(PaymentEnum.WECHAT_PAY.getType());
|
|
|
+ record.setOutTradeNo(request.outTradeNo);
|
|
|
+ record.setOutRefundNo(request.outRefundNo);
|
|
|
+ record.setRefundStatus("ABNORMAL");
|
|
|
+
|
|
|
+ // 金额信息
|
|
|
+ record.setTotalAmount(request.amount.total);
|
|
|
+ record.setRefundAmount(request.amount.refund);
|
|
|
+ record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
|
|
|
+
|
|
|
+ // 退款原因
|
|
|
+ record.setRefundReason(request.reason);
|
|
|
+
|
|
|
+ // 错误信息
|
|
|
+ if (e instanceof WXPayUtility.ApiException) {
|
|
|
+ WXPayUtility.ApiException apiException = (WXPayUtility.ApiException) e;
|
|
|
+ record.setErrorCode(String.valueOf(apiException.getErrorCode()));
|
|
|
+ record.setErrorMsg(apiException.getMessage());
|
|
|
+ } else {
|
|
|
+ record.setErrorMsg(e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 业务信息
|
|
|
+ if (params != null) {
|
|
|
+ if (params.containsKey("userId")) {
|
|
|
+ try {
|
|
|
+ record.setUserId(Integer.parseInt(params.get("userId")));
|
|
|
+ } catch (NumberFormatException ex) {
|
|
|
+ log.warn("解析userId失败: {}", params.get("userId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (params.containsKey("orderId")) {
|
|
|
+ record.setOrderId(params.get("orderId"));
|
|
|
+ }
|
|
|
+ if (params.containsKey("storeId")) {
|
|
|
+ try {
|
|
|
+ record.setStoreId(Integer.parseInt(params.get("storeId")));
|
|
|
+ } catch (NumberFormatException ex) {
|
|
|
+ log.warn("解析storeId失败: {}", params.get("storeId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 标准字段
|
|
|
+ record.setDeleteFlag(0);
|
|
|
+ record.setCreatedTime(new Date());
|
|
|
+ record.setRefundCreateTime(new Date());
|
|
|
+
|
|
|
+ return record;
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.error("构建RefundRecord对象失败", ex);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析微信支付返回的时间字符串(ISO 8601格式)
|
|
|
+ * 例如:2018-06-08T10:34:56+08:00
|
|
|
+ */
|
|
|
+ private Date parseWeChatTime(String timeStr) {
|
|
|
+ if (timeStr == null || timeStr.trim().isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ // 尝试解析ISO 8601格式
|
|
|
+ OffsetDateTime dateTime = OffsetDateTime.parse(timeStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
|
|
|
+ return Date.from(dateTime.toInstant());
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 如果ISO 8601解析失败,尝试其他格式
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ return sdf.parse(timeStr);
|
|
|
+ } catch (ParseException ex) {
|
|
|
+ log.warn("解析微信支付时间失败: {}", timeStr, ex);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|