You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1767 lines
69 KiB
C#

9 months ago
using Aop.Api;
using Aop.Api.Request;
using Aop.Api.Response;
using POSV.Bill;
using POSV.Component;
using POSV.Entity;
using POSV.MessageEvent;
using POSV.PayApi;
using POSV.Payment.Saobei;
using POSV.Payment.Saobei.Parameter;
using POSV.Payment.Saobei.Result;
using POSV.ShoppingCart;
using POSV.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace POSV.Business
{
public partial class MPayDialogForm : BusinessForm
{
/// <summary>
/// 支付支付通知,用于新版本食堂版,异步通知支付结果
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void PayResultPush(OrderObject _data, string payCode,bool succeed, Exception exception);
public PayResultPush payResultPush = null;
/// <summary>
/// PayStep3初始值
/// </summary>
private int initValue = 10;
/// <summary>
/// 是否等待用户输入支付密码
/// </summary>
private int timeout = 60;
//倒计时计时器
//private System.Timers.Timer timer = null;
private System.Windows.Forms.Timer timer = null;
private const int TIMER_INTERVAL = 1000;
private int count = 0;
private string SAOBEI_QRCodeType = "";
private string SAOBEI_PAYTIME = "";
private string SAOBEI_OrderId = "";
public static object _lock = new object();
public bool isPay = false;
/// <summary>
/// 订单对象
/// </summary>
private OrderObject _orderObject = null;
/// <summary>
/// 支付渠道
/// </summary>
private PayChannelEnum _payChannel = PayChannelEnum.;
public MPayDialogForm(OrderObject orderObject)
{
InitializeComponent();
MsgEvent.RemoveListener(Constant.PAY_AUTO_EVENT_NOTIFY, FinishedEventNotify);
MsgEvent.Receive(Constant.PAY_AUTO_EVENT_NOTIFY, FinishedEventNotify);
this._orderObject = orderObject;
this.lblTips.Text = string.Format(this.lblTips.Tag.ToString(), this._orderObject.ReceivableAmount - this._orderObject.ReceivedAmount);
this.initValue = 10;
this.SAOBEI_QRCodeType = "";
this.SAOBEI_OrderId = "";
this.isPay = false;
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
this.labelMessage.Text = "请扫描或输入支付码...";
this._orderObject = orderObject;
//初始化支付状态
this.isPay = false;
//微信支付不允许抹零
this._orderObject.MalingAmount = 0;
//微信支付不允许找零
this._orderObject.ChangeAmount = 0;
//将当前应收金额赋值
this.txtPayCode.Focus();
this.txtPayCode.SelectAll();
//支付结果查询的计时器
if (this.timer != null)
{
this.timer.Stop();
}
this.timer = null;
this.timer = new System.Windows.Forms.Timer();
this.timer.Interval = TIMER_INTERVAL;
this.timer.Tick += OnTimerTick;
}
protected virtual void FinishedEventNotify(object sender, MsgEventArgs args)
{
//整单状态
this._orderObject.OrderStatus = OrderStatus.;
//整单支付状态
this._orderObject.PaymentStatus = OrderPaymentStatus.;
//通知主界面结账成功
MsgEvent.Send(Constant.ORDER_PAYMENT_NOTIFY, this._orderObject);
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
//关闭界面
BillClose();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (DesignMode) return;
SetStyle(ControlStyles.Selectable, true);
this.txtPayCode.Multiline = false;
this.txtPayCode.Focus();
this.txtPayCode.SelectAll();
}
private void OnTimerTick(object sender, EventArgs e)
{
this.Invoke(new Action(() => {
switch (this._payChannel)
{
case PayChannelEnum.:
{
QueryPayChannel_0();
}
break;
case PayChannelEnum.:
{
QueryPayChannel_SAOBEI();
}
break;
}
}));
LOGGER.Info("查询支付结果中....");
}
/// <summary>
/// 操作是否验证通过
/// </summary>
private bool PaymentVerify = false;
/// <summary>
/// 当前的支付方式
/// </summary>
private PayMode CurrentPayMode = null;
private void OnKeyBoardTouchAfter(object sender, KeyboardExtEventArgs e)
{
switch (e.KeyCode)
{
case "clear":
//如果当前焦点控件是输入框
if (this.ActiveControl is TextBox)
{
var activeControl = this.ActiveControl as TextBox;
activeControl.Text = string.Empty;
}
break;
case "close":
{
OnCloseClick(sender, EventArgs.Empty);
}
break;
case "accept":
//验证通过
if (this.PaymentVerify && this.CurrentPayMode != null)
{
this.OnAcceptButtonClick(new TransparentEventArgs(TransparentAction.Accept, e.KeyCode, this._orderObject));
//保存扫描支付单号
SaveSaomaTicket(string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo), this.txtPayCode.Text.Trim());
switch (this._payChannel)
{
case PayChannelEnum.:
{
this.PayChannel_0();
}
break;
case PayChannelEnum.:
{
this.PayChannel_SAOBEI();
}
break;
}
this.Owner.Visible = false;
this.Visible = false;
}
break;
case "100":
{
InputSimulatorUtils.SendKey(KeyCodes.Map["1"]);
InputSimulatorUtils.SendKey(KeyCodes.Map["0"]);
InputSimulatorUtils.SendKey(KeyCodes.Map["0"]);
}
break;
case "50":
{
InputSimulatorUtils.SendKey(KeyCodes.Map["5"]);
InputSimulatorUtils.SendKey(KeyCodes.Map["0"]);
}
break;
case "20":
{
InputSimulatorUtils.SendKey(KeyCodes.Map["2"]);
InputSimulatorUtils.SendKey(KeyCodes.Map["0"]);
}
break;
case "10":
{
InputSimulatorUtils.SendKey(KeyCodes.Map["1"]);
InputSimulatorUtils.SendKey(KeyCodes.Map["0"]);
}
break;
default:
{
InputSimulatorUtils.SendKey(KeyCodes.Map[e.KeyCode]);
}
break;
}
}
private void OnCloseClick(object sender, EventArgs e)
{
BillClose();
}
public void BillClose() {
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this._orderObject = null;
this._payChannel = PayChannelEnum.;
if (this.timer != null)
{
this.timer.Stop();
this.timer = null;
LOGGER.Info("释放查询任务");
}
if (this.Owner != null)
{
this.Owner.Close();
}
this.Close();
}
private void OnKeyboardBefore(object sender, KeyboardExtEventArgs e)
{
this.PaymentVerify = false;
this.CurrentPayMode = null;
if (this._orderObject == null)
{
this.labelMessage.Text = "订单数据错误啦,请拍个照...";
this.PaymentVerify = false;
}
else
{
if (this._orderObject.PaidAmount > 0)
{
//获取扫描码
var payCode = this.txtPayCode.Text.Trim();
if (string.IsNullOrEmpty(payCode))
{
this.labelMessage.Text = "请扫描或输入支付码...";
this.PaymentVerify = false;
}
else
{
this.CurrentPayMode = GetPayMode(payCode);
if (this.CurrentPayMode == null)
{
this.labelMessage.Text = "支付码不合法或尚未适配...";
this.PaymentVerify = false;
}
else
{
this.labelMessage.Text = "扫描" + this.CurrentPayMode.Name + "付款码...";
this.PaymentVerify = VerifyInputValue(payCode, this.CurrentPayMode);
}
}
}
else
{
this.labelMessage.Text = "付款金额非法...";
this.PaymentVerify = false;
}
}
}
public void SaveSaomaTicket(string busNo, string authCode)
{
try
{
lock (Global.Instance.SyncLock)
{
using (var db = Global.Instance.OpenDataBase)
{
using (var transaction = db.GetTransaction())
{
//先查询核销记录是否存在
string querySql = "select * from pos_saoma_pay_ticket where orderNo = '{0}' and busType = 0 and storeId ='{1}'";
querySql = string.Format(querySql, busNo, Global.Instance.BusinessPlanLog.StoreId);
SaomaPayTicket saomaPayTicket = db.FirstOrDefault<SaomaPayTicket>(querySql);
//如果插入扫码支付记录
if (saomaPayTicket == null)
{
SaomaPayTicket entity = new SaomaPayTicket();
entity.Id = IdWorkerUtils.Instance.NextId();
entity.TenantId = Global.Instance.BusinessPlanLog.TenantId;
entity.StoreId = Global.Instance.BusinessPlanLog.StoreId;
entity.WorkerId = Global.Instance.BusinessPlanLog.WorkerId;
entity.ShiftNo = Global.Instance.BusinessPlanLog.No;
entity.OrderNo = busNo;
entity.AuthCode = authCode;
entity.Money = this._orderObject.ReceivableAmount - this._orderObject.ReceivedAmount;
entity.SerialNo = null;
entity.PayTime = this.SAOBEI_PAYTIME;
entity.BusType = 0;
entity.PayStatus = 0;
entity.PayChannel = (int)this._payChannel;
entity.PayTypeNo = this.CurrentPayMode.No;
entity.PayTypeName = this.CurrentPayMode.Name;
db.Save<SaomaPayTicket>(entity);
}
transaction.Complete();
LOGGER.Info("单号:[{0}]进行扫描支付,付款码{1}", busNo, authCode);
}
}
}
}
catch (Exception ex)
{
LOGGER.Error(ex);
}
}
public bool UpdateSaomaTicket(string orderNo, string serialNo, string payTime)
{
try
{
lock (Global.Instance.SyncLock)
{
using (var db = Global.Instance.OpenDataBase)
{
using (var transaction = db.GetTransaction())
{
string sql = "update pos_saoma_pay_ticket set serialNo ='{0}' ,payTime='{1}' where orderNo = '{2}' and busType = 0 and storeId ='{3}'";
sql = string.Format(sql, serialNo, payTime, orderNo, Global.Instance.BusinessPlanLog.StoreId);
db.Execute(sql, null);
transaction.Complete();
return false;
}
}
}
}
catch (Exception ex)
{
LOGGER.Error(ex);
}
return true;
}
/// <summary>
/// 根据扫描码适配支付方式
///
/// 支付宝授权码25~30开头的长度为16~24位的数字
///
/// 微信授权码 18位纯数字以10、11、12、13、14、15开头
///
/// </summary>
/// <param name="payCode"></param>
/// <returns></returns>
private PayMode GetPayMode(string payCode)
{
PayMode result = null;
if (!string.IsNullOrEmpty(payCode) && payCode.Length >= 16)
{
string prefix = payCode.Substring(0, 2);
switch (prefix)
{
case "10":
case "11":
case "12":
case "13":
case "14":
case "15":
{
//微信
result = OrderUtils.GetPayMode("05");
}
break;
case "25":
case "26":
case "27":
case "28":
case "29":
case "30":
{
//支付宝
result = OrderUtils.GetPayMode("04");
}
break;
}
}
return result;
}
#region 验证付款码、支付参数是否正确
private bool VerifyWxPay(string payCode, PayMode payMode)
{
if (string.IsNullOrEmpty(payCode) || payCode.Length != 18)
{
this.labelMessage.Text = "微信付款码非法...";
return false;
}
var parameter = payMode.Body;
string appId = parameter.ContainsKey("appid") ? parameter["appid"].ToString() : "";
string mchId = parameter.ContainsKey("mchid") ? parameter["mchid"].ToString() : "";
string subMchId = parameter.ContainsKey("submchid") ? parameter["submchid"].ToString() : "";
string key = parameter.ContainsKey("appsecret") ? parameter["appsecret"].ToString() : "";
if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(mchId) || string.IsNullOrEmpty(key))
{
this.labelMessage.Text = "微信支付参数配置不完整...";
return false;
}
return true;
}
private bool VerifyAliPay(string payCode, PayMode payMode)
{
if (string.IsNullOrEmpty(payCode) || (payCode.Length < 16 || payCode.Length > 24))
{
this.labelMessage.Text = "支付宝付款码非法...";
return false;
}
var parameter = payMode.Body;
string appId = parameter.ContainsKey("appid") ? parameter["appid"].ToString() : "";
string privateKey = parameter.ContainsKey("privatekey") ? parameter["privatekey"].ToString() : "";
string appAuthToken = parameter.ContainsKey("appAuthToken") ? parameter["appAuthToken"].ToString() : "";
string storeNo = parameter.ContainsKey("storeNo") ? parameter["storeNo"].ToString() : "";
if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(privateKey))
{
this.labelMessage.Text = "支付宝支付参数配置不完整...";
return false;
}
return true;
}
private bool VerifyInputValue(string payCode, PayMode payMode)
{
bool isVerify = false;
var parameter = payMode.Body;
if (parameter == null || parameter.Count == 0)
{
this.labelMessage.Text = "尚未配置支付参数...";
isVerify = false;
}
else
{
//获取支付渠道
string channel = (parameter.ContainsKey("channel") ? parameter["channel"].ToString() : "").ToLower();
//没有配置支付渠道,默认原生支付,兼容处理
if (string.IsNullOrEmpty(channel))
{
this._payChannel = PayChannelEnum.;
}
else
{
switch (channel)
{
case "alipay":
case "wxpay":
case "subalipay":
case "subwxpay":
{
this._payChannel = PayChannelEnum.;
}
break;
case "saobei":
{
this._payChannel = PayChannelEnum.;
}
break;
}
}
LOGGER.Info("适配到支付渠道:{0}", this._payChannel.ToString());
switch (this._payChannel)
{
case PayChannelEnum.:
{
switch (payMode.No)
{
case "04":
{
isVerify = VerifyAliPay(payCode, payMode);
}
break;
case "05":
{
isVerify = VerifyWxPay(payCode, payMode);
}
break;
default:
{
this.labelMessage.Text = "目前暂不支持该类型付款码...";
isVerify = false;
}
break;
}
}
break;
case PayChannelEnum.:
{
isVerify = VerifySAOBEIPay(payCode, payMode);
}
break;
}
}
return isVerify;
}
#endregion
#region 原生支付渠道
/// <summary>
/// 查询原生支付结果
/// </summary>
private void QueryPayChannel_0()
{
//进度条最大值
int maximum = 70;
initValue = initValue + 1;
//判断是否超时,如果超时终止计时器,界面反馈信息
if (initValue % maximum == 0)
{
//计时器停止
this.timer.Stop();
//修改结果颜色
//this.PayStep3.ProgressColors = new Color[] { Color.Red , Color.Red };
//发送结果通知
this.labelMessage.Text = "查询支付结果超时";
LOGGER.Warn("查询支付结果超时,系统无法确认本单已经支付,订单号<{0}>", this._orderObject.TradeNo);
//回掉通知
if (this.payResultPush != null)
{
this.payResultPush(this._orderObject, this.txtPayCode.Text, false, new Exception("查询支付结果超时"));
return;//不在启用窗口提示
}
var dialog = new DialogForm("支付结果", "没有查询到支付结果,系统无法确认本单已经支付", MessageBoxIcon.Warning, MessageBoxButtons.RetryCancel);
if (DialogResult.Retry == dialog.ShowDialog())
{
LOGGER.Warn("收银员手动选择重新查询一次结果,订单号<{0}>", this._orderObject.TradeNo);
this.labelMessage.Text = "等待确认扣款状态";
this.initValue = 10;
this.timeout = 60;
this.txtPayCode.Enabled = false;
this.billKeyboard1.LockCancel();
//启动定时器
this.timer.Start();
}
else
{
LOGGER.Warn("收银员手动放弃结果确认,订单号<{0}>", this._orderObject.TradeNo);
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
}
}
else
{
switch (this.CurrentPayMode.No)
{
case "04":
{
QueryAliPayResult();
}
break;
case "05":
{
QueryWxPayResult();
}
break;
}
}
}
/// <summary>
/// 原生支付
/// </summary>
private void PayChannel_0()
{
switch (this.CurrentPayMode.No)
{
case "04":
{
var isQuery = AliPayResult();
if (isQuery)
{
this.labelMessage.Text = "正在确认扣款状态";
this.initValue = 10;
this.timeout = 60;
this.txtPayCode.Enabled = false;
this.billKeyboard1.LockCancel();
//启动定时器
this.timer.Start();
}
else
{
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
}
}
break;
case "05":
{
var isQuery = WxPayResult();
//需要查询支付结果的两种情况1支付提示扣款成功2支付结果未知
if (isQuery)
{
this.labelMessage.Text = "等待确认扣款状态";
this.initValue = 10;
this.timeout = 60;
this.txtPayCode.Enabled = false;
this.billKeyboard1.LockCancel();
//启动定时器
this.timer.Start();
}
else
{
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
}
}
break;
}
}
#region 原生微信支付
/// <summary>
/// 微信支付,返回是否确认查询结果
/// </summary>
/// <returns></returns>
private bool WxPayResult()
{
bool isQuery = false;
try
{
string nonceStr = "x86" + ObjectId.GenerateNewStringId();
//门店编号
string deviceInfo = Global.Instance.Authc.StoreNo;
//门店名称
string body = Global.Instance.Worker.StoreInfo.Name + "-店内消费";
//商品详情
string detail = "";
//附加数据
string attach = "";
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
//订单总金额,单位为分,只能为整数
string totalFee = Convert.ToInt32((this._orderObject.ReceivableAmount - this._orderObject.ReceivedAmount) * 100).ToString();
//货币类型
string feeType = "CNY";
//终端IP
string spbillCreateIp = DeviceUtils.Instance.IPAddress;
//订单优惠标记
string goodsTag = "";
//扫码支付授权码设备读取用户微信中的条码或者二维码信息用户刷卡条形码规则18位纯数字以10、11、12、13、14、15开头
string authCode = this.txtPayCode.Text.Trim();
//禁止扫码框输入
this.txtPayCode.Enabled = false;
this.billKeyboard1.LockCancel();
//发起微信扣款请求
this.labelMessage.Text = "发起微信扣款请求";
var parameter = this.CurrentPayMode.Body;
//微信支付的配置参数
string appId = parameter["appid"].ToString();
string mchId = parameter["mchid"].ToString();
string subMchId = parameter["submchid"].ToString();
string key = parameter["appsecret"].ToString();
LOGGER.Info("订单[{0}]发起微信扣款[{1}]分", outTradeNo, totalFee);
var dataInfo = new WxPayV3MicroPayRequestData(appId, mchId, subMchId, key, nonceStr, deviceInfo, body, detail, attach, outTradeNo, totalFee, feeType, spbillCreateIp, goodsTag, authCode);
//扫码成功,提交被扫支付
MicropayResult result = WxPayV3.MicroPay(dataInfo);
LOGGER.Info("订单[{0}]微信扣款返回结果:{1}", outTradeNo, JsonUtils.Serialize(result));
if ("SUCCESS".Equals(result.return_code))
{
if ("SUCCESS".Equals(result.result_code))
{
//需要直接确认结果,无需等待输入密码
isQuery = true;
}
else
{
LOGGER.Warn("订单[{0}]微信支付[{1}]分发生错误,错误代码:{2},错误描述:{3}", outTradeNo, totalFee, result.err_code, result.err_code_des);
//Task.Factory.StartNew(new Action(() =>
//{
// Global.Instance.BugReport(new Exception("微信支付错误" + JsonUtils.Serialize(result)));
//}));
//解析错误代码
var errCode = WxPayErrCode.NONE;
Enum.TryParse(result.err_code, out errCode);
switch (errCode)
{
//支付结果未知,请立即调用被扫订单结果查询API查询当前订单状态并根据订单的状态决定下一步的操作
case WxPayErrCode.SYSTEMERROR:
{
this.labelMessage.Text = "网络超时,正在查询支付状态,请稍候...";
isQuery = true;
}
break;
case WxPayErrCode.BANKERROR:
{
this.labelMessage.Text = "银行端超时,正在查询支付状态,请稍候...";
isQuery = true;
}
break;
case WxPayErrCode.USERPAYING:
{
this.labelMessage.Text = "等待用户输入密码,请稍候...";
//需等待输入密码,需要确认结果
isQuery = true;
}
break;
case WxPayErrCode.ORDERPAID:
{
this.labelMessage.Text = "订单号重复支付,请使用新订单";
isQuery = false;
}
break;
case WxPayErrCode.AUTHCODEEXPIRE:
{
this.labelMessage.Text = "二维码过期,请用户在微信上刷新后再试";
isQuery = false;
}
break;
case WxPayErrCode.NOTENOUGH:
{
this.labelMessage.Text = "用户余额不足,请用户换卡支付";
isQuery = false;
}
break;
case WxPayErrCode.NOTSUPORTCARD:
{
this.labelMessage.Text = "不支持当前支付,请用户换卡或绑新卡支付";
isQuery = false;
}
break;
case WxPayErrCode.INVALID_REQUEST:
{
if (result.err_code_des == "201 商户订单号重复")
{
this.labelMessage.Text = "商户订单号重复,请消单后重新下单";
}
else
{
this.labelMessage.Text = result.err_code_des;
}
isQuery = false;
}
break;
default:
{
this.labelMessage.Text = result.err_code_des;
isQuery = false;
}
break;
}
}
}
else
{
LOGGER.Warn("订单[{0}]微信支付[{1}]分,微信支付网络通信错误:{0}", outTradeNo, totalFee, result.return_msg);
this.labelMessage.Text = result.return_msg;
//Task.Factory.StartNew(new Action(() =>
//{
// Global.Instance.BugReport(new Exception("微信支付错误2" + JsonUtils.Serialize(result)));
//}));
}
}
catch (Exception ex)
{
this.labelMessage.Text = "微信支付发生异常,开始确认支付结果";
LOGGER.Error(ex, "微信支付发生异常");
//发起支付发生异常,为了避免客户已经收到请求,开始支付,这里进行补救查询
isQuery = true;
}
return isQuery;
}
private void QueryWxPayResult()
{
try
{
var parameter = this.CurrentPayMode.Body;
string appId = parameter.ContainsKey("appid") ? parameter["appid"].ToString() : "";
string mchId = parameter.ContainsKey("mchid") ? parameter["mchid"].ToString() : "";
string subMchId = parameter.ContainsKey("submchid") ? parameter["submchid"].ToString() : "";
string key = parameter.ContainsKey("appsecret") ? parameter["appsecret"].ToString() : "";
string nonceStr = "x86" + ObjectId.GenerateNewStringId();
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
var dataInfo = new WxPayV3OrderQueryRequestData(appId, "", mchId, subMchId, "", nonceStr, outTradeNo, key);
var result = WxPayV3.OrderQuery(dataInfo);
LOGGER.Info("订单[{0}]微信(原生)查询支付结果:{1}", outTradeNo, JsonUtils.Serialize(result));
if ("SUCCESS".Equals(result.return_code))
{
if ("SUCCESS".Equals(result.result_code))
{
var tradeState = WxPayTradeState.NONE;
Enum.TryParse(result.trade_state, out tradeState);
switch (tradeState)
{
case WxPayTradeState.SUCCESS:
{
//计时器停止
this.timer.Stop();
//更新付款状态
lock (_lock)
{
if (!isPay)
{
isPay = true;
}
else
{
return;
}
}
//构建微信支付方式
this.AddWxPayType(result);
//微信支付更新核销状态
//UpdateSaoMa(outTradeNo);
//发送自动完成结账事件
this.labelMessage.Text = "微信支付成功";
//整单状态
this._orderObject.OrderStatus = OrderStatus.;
//整单支付状态
this._orderObject.PaymentStatus = OrderPaymentStatus.;
//通知主界面结账成功
MsgEvent.Send(Constant.ORDER_PAYMENT_NOTIFY, this._orderObject);
//MsgEvent.Send(Constant.PAY_AUTO_EVENT_NOTIFY, "微信支付成功");
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
//关闭界面
BillClose();
}
break;
case WxPayTradeState.USERPAYING:
{
string message = string.Format("{0}:{1}秒", "请提示用户输入支付密码", --timeout);
this.labelMessage.Text = message;
}
break;
case WxPayTradeState.NOTPAY:
case WxPayTradeState.PAYERROR:
{
//计时器停止
this.timer.Stop();
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
this.labelMessage.Text = "密码错误或未输入,请重新扫码";
}
break;
default:
{
string message = string.Format("{0}:{1}秒", result.trade_state_desc, --timeout);
this.labelMessage.Text = message;
}
break;
}
}
else
{
if (result.err_code == "ORDERNOTEXIST")
{
//计时器停止
this.timer.Stop();
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
this.labelMessage.Text = "微信:未收到该订单,请重新扫码";
}
else
{
string message = string.Format("正在确认微信扣款结果:{0}秒,{1}", --timeout, result.err_code_des);
this.labelMessage.Text = message;
}
}
}
else
{
LOGGER.Warn("查询支付结果网络通信错误:{0}", result.return_msg);
this.labelMessage.Text = result.return_msg;
//Task.Factory.StartNew(new Action(() =>
//{
// Global.Instance.BugReport(new Exception("查询微信支付结果网络通信错误" + JsonUtils.Serialize(result)));
//}));
}
}
catch (Exception ex)
{
LOGGER.Error(ex, "微信支付结果查询发生异常");
//Task.Factory.StartNew(new Action(() =>
//{
// Global.Instance.BugReport(ex);
//}));
}
}
private void AddWxPayType(OrderQueryResult result)
{
if (result != null && "SUCCESS".Equals(result.return_code) && "SUCCESS".Equals(result.result_code))
{
//构建微信支付方式
PayItem item = OrderUtils.ToPayItem(this.CurrentPayMode);
//租户ID
item.TenantId = Global.Instance.Authc.TenantId;
//订单号
item.OrderId = this._orderObject.Id;
item.TradeNo = this._orderObject.TradeNo;
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
item.OutTradeNo = outTradeNo;
//实收金额
item.PaidAmount = DecimalUtils.Fen2Yuan(result.total_fee);
//找零金额
item.ChangeAmount = Convert.ToDecimal(0.00);
//已收金额,真正意义上实际收款金额
item.Amount = item.PaidAmount;
item.CardNo = "";
item.TradeVoucherNo = result.transaction_id;
item.Memo = result.openid;
item.Subscribe = result.is_subscribe;
item.PayChannel = this._payChannel;
//微信支付状态
item.Status = (int)OrderPaymentStatus.; ;
item.StatusDesc = string.Format("微信支付<{0}>", result.result_code);
//将微信支付压入支付清单
this._orderObject.Pays.Add(item);
}
}
#endregion
#region 原生支付宝
/// <summary>
/// 支付宝付款,返回是否确认查询结果
/// </summary>
/// <returns></returns>
private bool AliPayResult()
{
bool isQuery = false;
try
{
var parameter = this.CurrentPayMode.Body;
string appId = parameter.ContainsKey("appid") ? parameter["appid"].ToString() : "";
string privateKey = parameter.ContainsKey("privatekey") ? parameter["privatekey"].ToString() : "";
string appAuthToken = parameter.ContainsKey("appAuthToken") ? parameter["appAuthToken"].ToString() : "";
string storeNo = parameter.ContainsKey("storeNo") ? parameter["storeNo"].ToString() : "";
string pid = parameter.ContainsKey("pid") ? parameter["pid"].ToString() : "";
string signType = parameter.ContainsKey("signType") ? parameter["signType"].ToString() : "RSA";
DefaultAopClient client = null;
if ("RSA2".Equals(signType))
{
client = new DefaultAopClient(Global.Instance.AliPayServerUrl, appId, privateKey, "json", "utf-8", "RSA2");
}
else
{
client = new DefaultAopClient(Global.Instance.AliPayServerUrl, appId, privateKey, false);
}
var request = new AlipayTradePayRequest();
var content = new Dictionary<string, object>();
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
content.Add("out_trade_no", outTradeNo);
content.Add("scene", "bar_code");// 支付场景-条码支付
content.Add("auth_code", this.txtPayCode.Text.Trim());// 支付授权码
content.Add("subject", Global.Instance.Worker.StoreInfo.Name + "-店内消费");// 订单标题
content.Add("body", Global.Instance.Authc.StoreNo + Global.Instance.Worker.StoreInfo.Name + "-店内消费");
content.Add("total_amount", this._orderObject.ReceivableAmount - this._orderObject.ReceivedAmount);
content.Add("operator_id", Global.Instance.Worker.No);
content.Add("store_id", Global.Instance.Authc.StoreNo);
content.Add("terminal_id", Global.Instance.Authc.PosNo);
if (!string.IsNullOrEmpty(appAuthToken))
{
var extend = new Dictionary<string, object>();
extend.Add("sys_service_provider_id", pid);
content.Add("extend_params", extend);
}
var goods = new List<Dictionary<string, object>>();
var details = new Dictionary<string, object>();
details.Add("goods_id", ObjectId.GenerateNewStringId());
details.Add("goods_name", "门店消费");
details.Add("quantity", "1");
details.Add("price", this._orderObject.ReceivableAmount - this._orderObject.ReceivedAmount);
details.Add("goods_category", ObjectId.GenerateNewStringId());
details.Add("body", "门店消费");
details.Add("show_url", "");
goods.Add(details);
content.Add("goods_detail", goods);
request.BizContent = JsonUtils.Serialize(content);
LOGGER.Info("订单[{0}]发起支付宝(原生)支付:<{1}>", outTradeNo, JsonUtils.Serialize(request));
AlipayTradePayResponse response = client.Execute(request, string.Empty, appAuthToken);
LOGGER.Info("订单[{0}]收到支付宝(原生)支付结果:<{1}>", outTradeNo, JsonUtils.Serialize(response));
if (!response.IsError && (response.Code == "10000" || response.Code == "10003" || response.Code == "20000"))
{
isQuery = true;
this.labelMessage.Text = "等待确认扣款结果...";
}
else
{
isQuery = false;
this.labelMessage.Text = string.Format("{0}<{1}>", response.SubMsg, response.SubCode);
}
}
catch (Exception ex)
{
LOGGER.Error(ex, "支付宝付款发生异常");
if (ex.Message.Contains("私钥格式错误"))
{
this.labelMessage.Text = "支付宝私钥配置错误...";
isQuery = false;
}
else
{
this.labelMessage.Text = "支付宝付款发生异常...";
isQuery = true;
}
//Task.Factory.StartNew(new Action(() =>
//{
// Global.Instance.BugReport(ex);
//}));
}
return isQuery;
}
private void QueryAliPayResult()
{
try
{
var parameter = this.CurrentPayMode.Body;
string appId = parameter.ContainsKey("appid") ? parameter["appid"].ToString() : "";
string privateKey = parameter.ContainsKey("privatekey") ? parameter["privatekey"].ToString() : "";
string appAuthToken = parameter.ContainsKey("appAuthToken") ? parameter["appAuthToken"].ToString() : "";
string storeNo = parameter.ContainsKey("storeNo") ? parameter["storeNo"].ToString() : "";
string signType = parameter.ContainsKey("signType") ? parameter["signType"].ToString() : "RSA";
DefaultAopClient client = null;
if ("RSA2".Equals(signType))
{
client = new DefaultAopClient(Global.Instance.AliPayServerUrl, appId, privateKey, "json", "utf-8", "RSA2");
}
else
{
client = new DefaultAopClient(Global.Instance.AliPayServerUrl, appId, privateKey, false);
}
var request = new AlipayTradeQueryRequest();
var content = new Dictionary<string, object>();
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
content.Add("out_trade_no", outTradeNo);
request.BizContent = JsonUtils.Serialize(content);
AlipayTradeQueryResponse response = client.Execute(request, string.Empty, appAuthToken);
LOGGER.Info("支付宝(原生)查询订单[{0}]支付状态结果:<{1}>", outTradeNo, JsonUtils.Serialize(response));
switch (response.TradeStatus)
{
case "TRADE_SUCCESS":
{
//计时器停止
this.timer.Stop();
//更新付款状态
lock (_lock)
{
if (!isPay)
{
isPay = true;
}
else
{
return;
}
}
//构建支付宝支付方式
this.AddAliPayType(response);
//发送自动完成结账事件
this.labelMessage.Text = "支付宝付款成功";
//整单状态
this._orderObject.OrderStatus = OrderStatus.;
//整单支付状态
this._orderObject.PaymentStatus = OrderPaymentStatus.;
//通知主界面结账成功
MsgEvent.Send(Constant.ORDER_PAYMENT_NOTIFY, this._orderObject);
if (this.payResultPush != null)
{
this.payResultPush(this._orderObject, this.txtPayCode.Text, true, null);
}
//MsgEvent.Send(Constant.PAY_AUTO_EVENT_NOTIFY, "支付宝付款成功");
//微信支付更新核销状态
//UpdateSaoMa(outTradeNo);
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
//关闭界面
BillClose();
}
break;
case "WAIT_BUYER_PAY":
{
this.labelMessage.Text = "等待买家付款...";
}
break;
default:
{
this.labelMessage.Text = "等待买家付款...";
}
break;
}
}
catch (Exception ex)
{
LOGGER.Error(ex, "支付宝结果查询发生异常");
}
}
private void AddAliPayType(AlipayTradeQueryResponse result)
{
if (result != null && "TRADE_SUCCESS".Equals(result.TradeStatus))
{
//构建微信支付方式
PayItem item = OrderUtils.ToPayItem(this.CurrentPayMode);
//租户ID
item.TenantId = Global.Instance.Authc.TenantId;
//订单号
item.OrderId = this._orderObject.Id;
//订单编号
item.TradeNo = this._orderObject.TradeNo;
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
item.OutTradeNo = outTradeNo;
//实收金额
item.PaidAmount = StringUtils.GetDecimal(result.TotalAmount);
//找零金额
item.ChangeAmount = Convert.ToDecimal(0.00);
//已收金额,真正意义上实际收款金额
item.Amount = item.PaidAmount;
item.CardNo = "";
item.TradeVoucherNo = result.TradeNo;
item.Memo = result.BuyerUserId;
item.Subscribe = string.Empty;
item.PayChannel = this._payChannel;
//支付宝支付状态
item.Status = (int)OrderPaymentStatus.;
item.StatusDesc = string.Format("支付宝付款<{0}>", result.TradeStatus);
//将支付宝支付压入支付清单
this._orderObject.Pays.Add(item);
}
}
#endregion
#endregion
#region 扫呗支付渠道
private void PayChannel_SAOBEI()
{
this.SAOBEI_QRCodeType = "";
this.SAOBEI_OrderId = "";
var saobeiPayResult = SAOBEIPayResult();
if (saobeiPayResult.Item1)
{
this.labelMessage.Text = "正在确认扣款状态";
this.initValue = 10;
this.timeout = 60;
this.txtPayCode.Enabled = false;
this.billKeyboard1.LockCancel();
//启动定时器
this.timer.Start();
}
else
{
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
}
}
private Tuple<bool, string, decimal> SAOBEIPayResult()
{
bool isQuery = false;
string orderId = "";
decimal payAmount = 0.00M;
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
var amount = this._orderObject.ReceivableAmount - this._orderObject.ReceivedAmount;
orderId = outTradeNo;
try
{
var parameter = this.CurrentPayMode.Body;
string merchant_no = parameter.ContainsKey("merchant_no") ? parameter["merchant_no"].ToString() : "";
string terminal_id = parameter.ContainsKey("terminal_id") ? parameter["terminal_id"].ToString() : "";
string signKey = parameter.ContainsKey("signKey") ? parameter["signKey"].ToString() : "";
string gatewayUrl = parameter.ContainsKey("gatewayUrl") ? parameter["gatewayUrl"].ToString() : "";
SaobeiPaymentParam request = new SaobeiPaymentParam();
request.Terminal_trace = outTradeNo.Replace("_", "");
request.Auth_no = this.txtPayCode.Text.Trim();
request.Total_fee = string.Format("{0}", Convert.ToInt16(amount * 100));
//支付方式 支付宝1、微信2
request.Pay_type = "020";
request.Terminal_time = DateTime.Now.ToString("yyyyMMddHHmmss");
this.SAOBEI_PAYTIME = request.Terminal_time;
if (this.CurrentPayMode.No == "05")
{
request.Pay_type = "010";
}
this.SAOBEI_QRCodeType = request.Pay_type;
var saobeiPayment = SaobeiUtils.SaobeiPayment(merchant_no, terminal_id, signKey, gatewayUrl, request);
if (saobeiPayment.Item1)
{
SaobeiPaymentResult saobeiPaymentResult = saobeiPayment.Item3;
//支付结果需要查询
isQuery = true;
this.SAOBEI_QRCodeType = saobeiPaymentResult.Pay_type;
this.SAOBEI_OrderId = saobeiPaymentResult.Out_trade_no;
UpdateSaomaTicket(outTradeNo, this.SAOBEI_OrderId, this.SAOBEI_PAYTIME);
this.labelMessage.Text = "等待确认扣款结果...";
}
else
{
isQuery = false;
this.SAOBEI_QRCodeType = "";
this.SAOBEI_OrderId = "";
UpdateSaomaTicket(outTradeNo, this.SAOBEI_OrderId, this.SAOBEI_PAYTIME);
this.labelMessage.Text = string.Format("{0}", saobeiPayment.Item2);
}
}
catch (Exception ex)
{
LOGGER.Error(ex, "扫呗支付订单[" + outTradeNo + "]金额[" + amount + "]发生异常!");
isQuery = false;
this.SAOBEI_QRCodeType = "";
this.SAOBEI_OrderId = "";
UpdateSaomaTicket(outTradeNo, this.SAOBEI_OrderId, this.SAOBEI_PAYTIME);
this.labelMessage.Text = "查不到支付状态,请使用手工核销功能...";
}
return new Tuple<bool, string, decimal>(isQuery, orderId, payAmount);
}
private void QueryPayChannel_SAOBEI()
{
//进度条最大值
int maximum = 70;
initValue = initValue + 1;
//判断是否超时,如果超时终止计时器,界面反馈信息
if (initValue % maximum == 0)
{
//计时器停止
this.timer.Stop();
//修改结果颜色
//this.PayStep3.ProgressColors = new Color[] { Color.Red , Color.Red };
//发送结果通知
this.labelMessage.Text = "查询支付结果超时";
LOGGER.Warn("查询支付结果超时,系统无法确认本单已经支付,订单号<{0}>", this._orderObject.TradeNo);
//回掉通知
if (this.payResultPush != null)
{
this.payResultPush(this._orderObject, this.txtPayCode.Text, false, new Exception(string.Format("查询支付结果超时,系统无法确认本单已经支付,订单号<{0}>", this._orderObject.TradeNo)));
return;
}
var dialog = new DialogForm("支付结果", "没有查询到支付结果,系统无法确认本单已经支付", MessageBoxIcon.Warning, MessageBoxButtons.RetryCancel);
if (DialogResult.Retry == dialog.ShowDialog())
{
LOGGER.Warn("收银员手动选择重新查询一次结果,订单号<{0}>", this._orderObject.TradeNo);
this.labelMessage.Text = "等待确认扣款状态";
this.initValue = 10;
this.timeout = 60;
this.txtPayCode.Enabled = false;
this.billKeyboard1.LockCancel();
//启动定时器
this.timer.Start();
}
else
{
LOGGER.Warn("收银员手动放弃结果确认,订单号<{0}>", this._orderObject.TradeNo);
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
}
}
else
{
this.QuerySAOBEIPayResult();
}
}
private void QuerySAOBEIPayResult()
{
try
{
var parameter = this.CurrentPayMode.Body;
string merchant_no = parameter.ContainsKey("merchant_no") ? parameter["merchant_no"].ToString() : "";
string terminal_id = parameter.ContainsKey("terminal_id") ? parameter["terminal_id"].ToString() : "";
string signKey = parameter.ContainsKey("signKey") ? parameter["signKey"].ToString() : "";
string gatewayUrl = parameter.ContainsKey("gatewayUrl") ? parameter["gatewayUrl"].ToString() : "";
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
SaobeiQueryParam request = new SaobeiQueryParam();
request.Pay_type = this.SAOBEI_QRCodeType;
request.Terminal_trace = outTradeNo.Replace("_", "");
request.Pay_trace = outTradeNo.Replace("_", "");
request.Pay_time = this.SAOBEI_PAYTIME;
request.Out_trade_no = this.SAOBEI_OrderId;
var saobeiPayment = SaobeiUtils.SaobeiQuery(merchant_no, terminal_id, signKey, gatewayUrl, request);
if (saobeiPayment.Item1)
{
if (saobeiPayment.Item3 != null && "SUCCESS".Equals(saobeiPayment.Item3.Trade_state))
{
//计时器停止
this.timer.Stop();
//更新付款状态
lock (_lock)
{
if (!isPay)
{
isPay = true;
}
else
{
return;
}
}
//发送消息通知显示文本
this.labelMessage.Text = "扫呗支付成功";
//构建支付方式
this.AddSAOBEIPayType(saobeiPayment.Item3);
//整单状态
this._orderObject.OrderStatus = OrderStatus.;
//整单支付状态
this._orderObject.PaymentStatus = OrderPaymentStatus.;
//通知主界面结账成功
MsgEvent.Send(Constant.ORDER_PAYMENT_NOTIFY, this._orderObject);
//回掉通知
if (this.payResultPush != null)
{
this.payResultPush(this._orderObject, this.txtPayCode.Text, true, null);
}
//发送自动完成结账事件
//MsgEvent.Send(Constant.PAY_AUTO_EVENT_NOTIFY, "扫呗支付付款成功");
//微信支付更新核销状态
//UpdateSaoMa(outTradeNo);
//清理支付码
this.txtPayCode.Text = string.Empty;
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
//关闭界面
BillClose();
}
else
{
this.labelMessage.Text = "查询结果:" + saobeiPayment.Item2;
}
}
else
{
//明确失败,结束查询
//计时器停止
this.timer.Stop();
//允许扫码框输入
this.txtPayCode.Enabled = true;
this.billKeyboard1.UnLock();
this.txtPayCode.Text = string.Empty;
this.txtPayCode.Focus();
this.labelMessage.Text = saobeiPayment.Item2;
//回掉通知
if (this.payResultPush != null)
{
this.payResultPush(this._orderObject, this.txtPayCode.Text, false, new Exception(saobeiPayment.Item2));
}
}
}
catch (Exception ex)
{
LOGGER.Error(ex, "银联商务支付付款结果查询发生异常");
}
}
private void AddSAOBEIPayType(SaobeiQueryResult queryParam)
{
if (queryParam != null && "SUCCESS".Equals(queryParam.Trade_state))
{
//构建支付方式
PayItem item = OrderUtils.ToPayItem(this.CurrentPayMode);
//租户ID
item.TenantId = Global.Instance.Authc.TenantId;
//订单号
item.OrderId = this._orderObject.Id;
//订单编号
item.TradeNo = this._orderObject.TradeNo;
//商户订单号
string outTradeNo = string.Format("{0}_{1}", this._orderObject.StoreNo, this._orderObject.TradeNo);
item.OutTradeNo = outTradeNo;
//实收金额
item.PaidAmount = Convert.ToDecimal(StringUtils.GetDecimal(queryParam.Total_fee) / 100);
//找零金额
item.ChangeAmount = Convert.ToDecimal(0.00);
//已收金额,真正意义上实际收款金额
item.Amount = item.PaidAmount;
item.CardNo = "";
item.TradeVoucherNo = queryParam.Out_trade_no;//利楚唯一订单号
item.Memo = this._payChannel.ToString();
item.Subscribe = string.Empty;
item.PayChannel = this._payChannel;
//支付状态
item.Status = (int)OrderPaymentStatus.;
item.StatusDesc = string.Format("{0}", queryParam.Trade_state);
//将支付宝支付压入支付清单
this._orderObject.Pays.Add(item);
}
}
private bool VerifySAOBEIPay(string payCode, PayMode payMode)
{
if (string.IsNullOrEmpty(payCode))
{
this.labelMessage.Text = "付款码非法...";
return false;
}
var parameter = payMode.Body;
//扫呗支付参数
string merchant_no = parameter.ContainsKey("merchant_no") ? parameter["merchant_no"].ToString() : "";
string terminal_id = parameter.ContainsKey("terminal_id") ? parameter["terminal_id"].ToString() : "";
string signKey = parameter.ContainsKey("signKey") ? parameter["signKey"].ToString() : "";
string gatewayUrl = parameter.ContainsKey("gatewayUrl") ? parameter["gatewayUrl"].ToString() : "";
if (string.IsNullOrEmpty(merchant_no) || string.IsNullOrEmpty(terminal_id) || string.IsNullOrEmpty(signKey) || string.IsNullOrEmpty(gatewayUrl))
{
this.labelMessage.Text = "扫呗支付参数配置不完整...";
return false;
}
return true;
}
#endregion
private void OnPayCodeEnterClick(object sender, Component.EnterEventArg e)
{
this.OnKeyboardBefore(this.billKeyboard1, new KeyboardExtEventArgs("accept"));
if (this.PaymentVerify)
{
this.OnKeyBoardTouchAfter(this.billKeyboard1, new KeyboardExtEventArgs("accept"));
}
}
private void OnPayCodeValueChanged(object sender, Component.EnterEventArg e)
{
string inputString = e.Value;
if (!string.IsNullOrEmpty(inputString))
{
int intputLength = inputString.Length;
}
else
{
}
}
private void OnPayCodeCheckClick(object sender, TouchEventArgs e)
{
//打开提示界面
var form = new FastMPayChckForm(this._orderObject, this._orderObject.ReceivableAmount - this._orderObject.ReceivedAmount);
form.ShowDialog();
}
}
}