Commit 58a704cf authored by 张永's avatar 张永

需求 #14327

parent 07c8f8ef
......@@ -13,6 +13,8 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.build.timestamp>${maven.build.timestamp}</maven.build.timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
<xstream.version>1.4.1</xstream.version>
<jdom.version>2.0.2</jdom.version>
</properties>
<parent>
......@@ -103,6 +105,17 @@
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>${xstream.version}</version>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>${jdom.version}</version>
</dependency>
<!-- 阿里云 start -->
<dependency>
<groupId>com.aliyun</groupId>
......
......@@ -371,4 +371,23 @@ public class OrderController {
return Rjx.jsonOk().toJson();
}
@ApiOperation("提交补款记录")
@RequestMapping(value = "/createSalesOrderDebtPay",method=RequestMethod.POST)
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name="shopNO", dataType = "String", required = true),
@ApiImplicitParam(paramType = "query", name="salesOrderID", dataType = "String", required = true),
@ApiImplicitParam(paramType = "query", name="subOrderID", dataType = "String", required = true),
@ApiImplicitParam(paramType = "query", name="payTypeID", dataType = "String", required = true),
@ApiImplicitParam(paramType = "query", name="payType", dataType = "String", required = true),
@ApiImplicitParam(paramType = "query", name="payAmount", dataType = "String", required = true),
@ApiImplicitParam(paramType = "query", name="payCardNO", dataType = "String", required = true),
@ApiImplicitParam(paramType = "query", name = "terminal", dataType = "String", required = true, value = "终端标识", defaultValue = "wechat"),
@ApiImplicitParam(paramType = "query", name = "langID", dataType = "String", required = true, value = "语言", defaultValue = "936"),
})
public String createSalesOrderDebtPay(String shopNO, String salesOrderID,String subOrderID,String payTypeID,String payType,String payAmount,String payCardNO) {
List<Map<String,Object>> list = orderService.createSalesOrderDebtPay(shopNO, salesOrderID,subOrderID,payTypeID,payType,payAmount,payCardNO);
return Rjx.jsonOk().set("list", list).toJson();
}
}
package com.egolm.shop.api;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dom4j.DocumentHelper;
import org.json.XML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.egolm.common.DateUtil;
import com.egolm.common.GsonUtil;
import com.egolm.common.HttpsUtil;
import com.egolm.common.StringUtil;
import com.egolm.shop.api.service.OrderService;
import com.egolm.shop.bean.WxPayResponse;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@Api(tags={"支付相关接口"})
@RestController
@RequestMapping("pay")
public class PayNotifyController {
protected final Logger logger = LoggerFactory.getLogger(PayNotifyController.class);
@Autowired
private OrderService orderService;
@ApiOperation("支付回调通知接口")
@RequestMapping(value = "/callback", method = RequestMethod.POST)
public Object callback(HttpServletRequest req, HttpServletResponse resp) {
WxPayResponse postData = null;
try {
// 转换微信post过来的xml内容
ServletInputStream in = req.getInputStream();
XStream xs = new XStream(new DomDriver());
xs.setClassLoader(WxPayResponse.class.getClassLoader());
xs.alias("xml", WxPayResponse.class);
String xmlMsg = StringUtil.inputStream2String(in);
logger.info("接收到回调信息 {}",xmlMsg);
postData = (WxPayResponse) xs.fromXML(xmlMsg);
List<Map<String, Object>> returnMap = orderService.payCallback(postData);
logger.info("处理回调信息结果 {}",GsonUtil.toJson(returnMap));
} catch (Exception e) {
e.printStackTrace();
}
return "success";
}
public static void main(String[] args) {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n" +
"<xml>\r\n" +
"<appid><![CDATA[wxb934522389d47388]]></appid>\r\n" +
"<bank_type><![CDATA[OTHERS]]></bank_type>\r\n" +
"<cash_fee><![CDATA[1]]></cash_fee>\r\n" +
"<fee_type><![CDATA[CNY]]></fee_type>\r\n" +
"<is_subscribe><![CDATA[N]]></is_subscribe>\r\n" +
"<mch_id><![CDATA[829004816000155]]></mch_id>\r\n" +
"<nonce_str><![CDATA[1677568978015]]></nonce_str>\r\n" +
"<openid><![CDATA[o-8AO5C3MIpEWiMQqlWTSvbnUhtQ]]></openid>\r\n" +
"<out_trade_no><![CDATA[16775689551728_967438]]></out_trade_no>\r\n" +
"<pay_type><![CDATA[120]]></pay_type>\r\n" +
"<result_code><![CDATA[SUCCESS]]></result_code>\r\n" +
"<return_code><![CDATA[SUCCESS]]></return_code>\r\n" +
"<sign><![CDATA[017EC9868E1BD27A1BF786F69605E8FF]]></sign>\r\n" +
"<time_end><![CDATA[20230228152257]]></time_end>\r\n" +
"<total_fee><![CDATA[1]]></total_fee>\r\n" +
"<trade_type><![CDATA[LCSW_+010]]></trade_type>\r\n" +
"<transaction_id><![CDATA[4200001803202302280221844190]]></transaction_id>\r\n" +
"</xml>";
String url = "http://localhost:30005/shop/pay/callback";
String result = HttpsUtil.doPostForXml(url, xml);
System.out.println(result);
}
}
\ No newline at end of file
......@@ -8,6 +8,7 @@ import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.egolm.common.jdbc.Page;
import com.egolm.shop.bean.RespObject;
import com.egolm.shop.bean.WxPayResponse;
public interface OrderService {
......@@ -40,5 +41,8 @@ public interface OrderService {
public RespObject queryRefundOrderList(String orgNo, String shopNo, Integer refundStatus, String langID, Page page);
public List<Map<String, Object>> createSalesOrderDebtPay(String shopNO, String salesOrderID,String subOrderID,String payTypeID,String payType,String payAmount,String payCardNO) ;
public List<Map<String, Object>> payCallback(WxPayResponse postData);
}
......@@ -9,6 +9,8 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.UncategorizedSQLException;
......@@ -29,11 +31,13 @@ import com.egolm.shop.api.service.OrderService;
import com.egolm.shop.bean.RespObject;
import com.egolm.shop.bean.TSalesOrder;
import com.egolm.shop.bean.TSalesOrderDtl;
import com.egolm.shop.bean.WxPayResponse;
import com.egolm.shop.common.XException;
import com.egolm.shop.common.utils.I18NUtils;
import com.qcloud.cos.utils.DateUtils;
@Service
public class OrderServiceImpl implements OrderService {
private static final Log logger = LogFactory.getLog(OrderServiceImpl.class);
@Autowired
private JdbcTemplate jdbcTemplate;
......@@ -1042,4 +1046,38 @@ public class OrderServiceImpl implements OrderService {
return new RespObject(result);
}
//提交补款记录
public List<Map<String, Object>> createSalesOrderDebtPay(String shopNO, String salesOrderID,String subOrderID,String payTypeID,String payType,String payAmount,String payCardNO) {
List<Map<String, Object>> returnMap = new ArrayList<Map<String, Object>>();
try {
String sql = "exec up_CreateSalesOrderDebtPay '"+shopNO+"' ,'"+salesOrderID+"' , '"+subOrderID+"' , '"+payTypeID+"' , '"+payType+"' , '"+payAmount+"' , '"+payCardNO+"' ";
returnMap = jdbcTemplate.executeMutil(sql).getDatas().get(0);
return returnMap;
} catch (Exception e) {
e.printStackTrace();
}
return returnMap;
}
//回调标记已补款
public List<Map<String, Object>> payCallback(WxPayResponse postData) {
List<Map<String, Object>> returnMap = new ArrayList<Map<String, Object>>();
String OutTradeNO = postData.getOut_trade_no();
String TransactionID = postData.getTransaction_id();
String PayCardNO = postData.getOpenid();
String PayAmount= postData.getTotal_fee().toString();
String PayDate = postData.getTime_end();
String PayChannel = postData.getPay_type();
String paydate = DateUtil.format(DateUtil.parse(PayDate,DateUtil.FMT_YYYYMMddHHMMSS),DateUtil.FMT_DATE_SECOND);
try {
String sql = "exec up_B2BSalesOrderDebtPayCallBack '"+OutTradeNO+"' ,'"+TransactionID+"' , '"+PayCardNO+"' , '"+PayAmount+"' , '"+paydate+"' , '"+PayChannel+"' ";
returnMap = jdbcTemplate.executeMutil(sql).getDatas().get(0);
} catch (Exception e) {
e.printStackTrace();
}
return returnMap;
}
}
package com.egolm.shop.bean;
import java.io.Serializable;
import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* @Title: 微信支付返回
* @Description: https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=
* 9_10 &index=1
* @author 張永
* @date 2016年8月10
* @version V1.0
*
*/
@XmlRootElement(name="xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxPayResponse implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4871369633747793198L;
@XStreamAlias("order_flow_no")
private String order_flow_no; // 订单流水号
@XStreamAlias("appid")
private String appid; // 调用接口提交的公众账号ID
@XStreamAlias("mch_id")
private String mch_id; // 商户号
@XStreamAlias("sub_mch_id")
private String sub_mch_id; // 子商户号
@XStreamAlias("sub_appid")
private String sub_appid;// 子商户公众账号ID
@XStreamAlias("sub_openid")
private String sub_openid;// 用户子标识
@XStreamAlias("device_info")
private String device_info;// 设备号
@XStreamAlias("nonce_str")
private String nonce_str;// 随机字符串
@XStreamAlias("sign")
private String sign;// 签名
@XStreamAlias("result_code")
private String result_code; // 业务结果
@XStreamAlias("err_code")
private String err_code; // 错误代码
@XStreamAlias("err_code_des")
private String err_code_des;// 错误代码描述
@XStreamAlias("openid")
private String openid; // 用户标识
@XStreamAlias("is_subscribe")
private String is_subscribe; // 是否关注公众账号
@XStreamAlias("trade_type")
private String trade_type; // 交易类型
@XStreamAlias("bank_type")
private String bank_type; // 付款银行
@XStreamAlias("fee_type")
private String fee_type; // 货币类型
@XStreamAlias("total_fee")
private BigDecimal total_fee; // 总金额
@XStreamAlias("cash_fee_type")
private String cash_fee_type; // 现金支付货币类型
@XStreamAlias("cash_fee")
private BigDecimal cash_fee; // 现金支付金额
@XStreamAlias("coupon_fee")
private BigDecimal coupon_fee; // 代金券或立减优惠金额
@XStreamAlias("transaction_id")
private String transaction_id; // 微信支付订单号
@XStreamAlias("out_trade_no")
private String out_trade_no; // 商户订单号
@XStreamAlias("attach")
private String attach; // 商家数据包
@XStreamAlias("time_end")
private String time_end; // 支付完成时间
@XStreamAlias("sub_is_subscribe")
private String sub_is_subscribe; // 是否关注子公众账号
@XStreamAlias("return_code")
private String return_code; // 返回状态码 SUCCESS/FAIL
// 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
@XStreamAlias("return_msg")
private String return_msg; // 返回信息
@XStreamAlias("auth_code")
private String auth_code;// 扫码支付授权码,设备读取用户微信中的条码或者二维码信息
@XStreamAlias("operator_no")
private String operator_no;// 交易发起人在平台内的标识,如司机APP则operatorNo是APP的登录用户名
@XStreamAlias("server_host")
private String server_host; // 处理交易订单的服务器的IP地址
@XStreamAlias("start_time")
private String start_time; // 交易开始时间
@XStreamAlias("randByNum")
private String randByNum;// 交易订单随机码
@XStreamAlias("body")
private String body;// 商品或支付单简要描述
// 交易流水查询时返回
@XStreamAlias("trade_state")
private String trade_state;// 交易状态
@XStreamAlias("trade_state_desc")
private String trade_state_desc;// 交易状态描述
@XStreamAlias("tradeTypeDesc")
private String tradeTypeDesc;
@XStreamAlias("recall")
private String recall; // 发起刷卡支付撤销订单时返回,是否需要继续调用撤销,Y-需要,N-不需要
@XStreamAlias("flow_type")
private String flow_type; // 交易流水类型 --PAY 支付, --QUERY 查询, --REVERSE 撤单
@XStreamAlias("out_refund_no")
private String out_refund_no;// 商户退款单号
@XStreamAlias("refund_id")
private String refund_id;// 微信退款单号
@XStreamAlias("refund_fee")
private String refund_fee;// 申请退款金额
@XStreamAlias("refund_channel")
private String refund_channel;
@XStreamAlias("coupon_refund_fee")
private String coupon_refund_fee;
@XStreamAlias("coupon_refund_count")
private String coupon_refund_count;
@XStreamAlias("cash_refund_fee")
private String cash_refund_fee;
@XStreamAlias("product_id")
private String product_id;
@XStreamAlias("coupon_count")
private String coupon_count;
@XStreamAlias("coupon_fee_0")
private String coupon_fee_0;
@XStreamAlias("coupon_id_0")
private String coupon_id_0;
@XStreamAlias("coupon_fee_1")
private String coupon_fee_1;
@XStreamAlias("coupon_id_1")
private String coupon_id_1;
@XStreamAlias("coupon_fee_2")
private String coupon_fee_2;
@XStreamAlias("coupon_id_2")
private String coupon_id_2;
@XStreamAlias("coupon_fee_3")
private String coupon_fee_3;
@XStreamAlias("coupon_id_3")
private String coupon_id_3;
@XStreamAlias("prepay_id")
private String prepay_id;
@XStreamAlias("code_url")
private String code_url;
@XStreamAlias("coupon_refund_fee_0")
private String coupon_refund_fee_0;
@XStreamAlias("coupon_refund_id_0")
private String coupon_refund_id_0;
@XStreamAlias("sign_type")
private String sign_type;
@XStreamAlias("pay_type")
private String pay_type; //自定义的
public String getPay_type() {
return pay_type;
}
public void setPay_type(String pay_type) {
this.pay_type = pay_type;
}
public String getSign_type() {
return sign_type;
}
public void setSign_type(String sign_type) {
this.sign_type = sign_type;
}
public String getCoupon_refund_fee_0() {
return coupon_refund_fee_0;
}
public void setCoupon_refund_fee_0(String coupon_refund_fee_0) {
this.coupon_refund_fee_0 = coupon_refund_fee_0;
}
public String getCoupon_refund_id_0() {
return coupon_refund_id_0;
}
public void setCoupon_refund_id_0(String coupon_refund_id_0) {
this.coupon_refund_id_0 = coupon_refund_id_0;
}
public String getCoupon_fee_1() {
return coupon_fee_1;
}
public void setCoupon_fee_1(String coupon_fee_1) {
this.coupon_fee_1 = coupon_fee_1;
}
public String getCoupon_id_1() {
return coupon_id_1;
}
public void setCoupon_id_1(String coupon_id_1) {
this.coupon_id_1 = coupon_id_1;
}
public String getCoupon_fee_2() {
return coupon_fee_2;
}
public void setCoupon_fee_2(String coupon_fee_2) {
this.coupon_fee_2 = coupon_fee_2;
}
public String getCoupon_id_2() {
return coupon_id_2;
}
public void setCoupon_id_2(String coupon_id_2) {
this.coupon_id_2 = coupon_id_2;
}
public String getCoupon_fee_3() {
return coupon_fee_3;
}
public void setCoupon_fee_3(String coupon_fee_3) {
this.coupon_fee_3 = coupon_fee_3;
}
public String getCoupon_id_3() {
return coupon_id_3;
}
public void setCoupon_id_3(String coupon_id_3) {
this.coupon_id_3 = coupon_id_3;
}
public String getCode_url() {
return code_url;
}
public void setCode_url(String code_url) {
this.code_url = code_url;
}
public String getPrepay_id() {
return prepay_id;
}
public void setPrepay_id(String prepay_id) {
this.prepay_id = prepay_id;
}
public String getOut_refund_no() {
return out_refund_no;
}
public void setOut_refund_no(String out_refund_no) {
this.out_refund_no = out_refund_no;
}
public String getRefund_id() {
return refund_id;
}
public void setRefund_id(String refund_id) {
this.refund_id = refund_id;
}
public String getRefund_fee() {
return refund_fee;
}
public void setRefund_fee(String refund_fee) {
this.refund_fee = refund_fee;
}
public String getRefund_channel() {
return refund_channel;
}
public void setRefund_channel(String refund_channel) {
this.refund_channel = refund_channel;
}
public String getCoupon_refund_fee() {
return coupon_refund_fee;
}
public void setCoupon_refund_fee(String coupon_refund_fee) {
this.coupon_refund_fee = coupon_refund_fee;
}
public String getCoupon_refund_count() {
return coupon_refund_count;
}
public void setCoupon_refund_count(String coupon_refund_count) {
this.coupon_refund_count = coupon_refund_count;
}
public String getCash_refund_fee() {
return cash_refund_fee;
}
public void setCash_refund_fee(String cash_refund_fee) {
this.cash_refund_fee = cash_refund_fee;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getRandByNum() {
return randByNum;
}
public void setRandByNum(String randByNum) {
this.randByNum = randByNum;
}
public String getSub_openid() {
return sub_openid;
}
public void setSub_openid(String sub_openid) {
this.sub_openid = sub_openid;
}
public String getSub_appid() {
return sub_appid;
}
public void setSub_appid(String sub_appid) {
this.sub_appid = sub_appid;
}
public String getSub_mch_id() {
return sub_mch_id;
}
public void setSub_mch_id(String sub_mch_id) {
this.sub_mch_id = sub_mch_id;
}
public String getReturn_code() {
return return_code;
}
public void setReturn_code(String return_code) {
this.return_code = return_code;
}
public String getReturn_msg() {
return return_msg;
}
public void setReturn_msg(String return_msg) {
this.return_msg = return_msg;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public String getCoupon_count() {
return coupon_count;
}
public void setCoupon_count(String coupon_count) {
this.coupon_count = coupon_count;
}
public String getCoupon_fee_0() {
return coupon_fee_0;
}
public void setCoupon_fee_0(String coupon_fee_0) {
this.coupon_fee_0 = coupon_fee_0;
}
public String getCoupon_id_0() {
return coupon_id_0;
}
public void setCoupon_id_0(String coupon_id_0) {
this.coupon_id_0 = coupon_id_0;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getResult_code() {
return result_code;
}
public void setResult_code(String result_code) {
this.result_code = result_code;
}
public String getErr_code() {
return err_code;
}
public void setErr_code(String err_code) {
this.err_code = err_code;
}
public String getErr_code_des() {
return err_code_des;
}
public void setErr_code_des(String err_code_des) {
this.err_code_des = err_code_des;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getIs_subscribe() {
return is_subscribe;
}
public void setIs_subscribe(String is_subscribe) {
this.is_subscribe = is_subscribe;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getBank_type() {
return bank_type;
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public String getCash_fee_type() {
return cash_fee_type;
}
public void setCash_fee_type(String cash_fee_type) {
this.cash_fee_type = cash_fee_type;
}
public BigDecimal getTotal_fee() {
return total_fee;
}
public void setTotal_fee(BigDecimal total_fee) {
this.total_fee = total_fee;
}
public BigDecimal getCash_fee() {
return cash_fee;
}
public void setCash_fee(BigDecimal cash_fee) {
this.cash_fee = cash_fee;
}
public BigDecimal getCoupon_fee() {
return coupon_fee;
}
public void setCoupon_fee(BigDecimal coupon_fee) {
this.coupon_fee = coupon_fee;
}
public String getTransaction_id() {
return transaction_id;
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getSub_is_subscribe() {
return sub_is_subscribe;
}
public void setSub_is_subscribe(String sub_is_subscribe) {
this.sub_is_subscribe = sub_is_subscribe;
}
public String getOrder_flow_no() {
return order_flow_no;
}
public void setOrder_flow_no(String order_flow_no) {
this.order_flow_no = order_flow_no;
}
public String getAuth_code() {
return auth_code;
}
public void setAuth_code(String auth_code) {
this.auth_code = auth_code;
}
public String getOperator_no() {
return operator_no;
}
public void setOperator_no(String operator_no) {
this.operator_no = operator_no;
}
public String getServer_host() {
return server_host;
}
public void setServer_host(String server_host) {
this.server_host = server_host;
}
public String getTime_end() {
return time_end;
}
public void setTime_end(String time_end) {
this.time_end = time_end;
}
public String getStart_time() {
return start_time;
}
public void setStart_time(String start_time) {
this.start_time = start_time;
}
public String getTradeTypeDesc() {
return tradeTypeDesc;
}
public void setTradeTypeDesc(String tradeTypeDesc) {
this.tradeTypeDesc = tradeTypeDesc;
}
public String getTrade_state() {
return trade_state;
}
public void setTrade_state(String trade_state) {
this.trade_state = trade_state;
}
public String getTrade_state_desc() {
return trade_state_desc;
}
public void setTrade_state_desc(String trade_state_desc) {
this.trade_state_desc = trade_state_desc;
}
public String getFlow_type() {
return flow_type;
}
public void setFlow_type(String flow_type) {
this.flow_type = flow_type;
}
public String getRecall() {
return recall;
}
public void setRecall(String recall) {
this.recall = recall;
}
public String getProduct_id() {
return product_id;
}
public void setProduct_id(String product_id) {
this.product_id = product_id;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment