DiscuzNT 商品交易插件设计之[线上交易]—支付宝

在上一篇文章中,介绍了商品交易线下交易流程,这一篇则重点介绍一下线上支付宝交易流程。在开始今天的正文之前,有必要介绍一下关于支付宝交易信息通知的一些内容,因为我们的商品交易插件使用了其中的一种通知方式。 在支付宝系统中,当交易发生时,根据系统的“处理方式”分为两类: 1.立即返回处理结果,即用户在支付宝页面完成操作后,支付宝将处理结果立即返回给合作伙伴的下一步操作页面,让用户继续完成整个操作流程。所以调用这类接口时,必须传递参

数return_url(即合作伙伴的下一个操作页面)。如下图所示:

2.异步返回处理结果,即用户从合作伙伴页面跳转到支付宝页面后,在支付宝完成最后操作,用户不用再回到合作伙伴页面。这类接口通常是通过通知接口异步获得处理结果。如果需要异步返回结果,那么必须传递notify_url参数,以指定通知返回的地址。如果不需要异步返

回结果,那么可以不用传递notify_url参数。如下图所示:

因为第一种方式要将系统的交易通知信息添加到跳转链接并转向回用户页面,给人感觉像是从一个系统忽然又变到另一个系统,让是感觉怪怪的,另外就是通知的时间有可能不是当时而是要过上一段时间,导致买卖双方要再回到合作伙伴页面,这样用户体验不是太好。所以我使用了第二种方式(当然discuz也是用的这一种方式)。为了便于调用同时也为了支持其他第三方的支付平台,这里进行了接口定义。同时对生成

支付宝url链接串的操作也进行了相应封装。请先看一下相关的类关系图:

相应的类所在位置如下图所示:

其中的IPayment是支付接口,目前因为只支持支付宝一种在线支付方式,所以只有一个派生类:AliPayment。其采用Singleton模式进行类实例化,并提供了创建虚拟商品(digital)和实物商品交易url链接串的方法,分别是:

CreateDigitalGoodsTradeUrl()//虚拟商品CreateNormalGoodsTradeUrl()//实物商品 这里将商品划分为虚拟和实物商品交易主要是对应了支付定的sdk文档,另外因为这两种交易在属性上有着部分重叠的地方,因此这里让DigitalTrade作为NormallTrade的父类,并将其继承到ITrade接口下,而该接口目前暂时为空接口(没有相应的属性和方法)。要注意的是在这里我将物流信息类分离出来,而这里做的原因是支付宝支持采用多种物流方式购买商品的行为,其类声明如下所示:

///<summary>///物流信息类///</summary>publicclassLogisticsInfo{privatestring_logistics_type;privatedecimal_logistics_fee;privatestring_logistics_payment;///<summary>///构造函数///</summary>///<paramname=”logistics_type”>物流类型:VIRTUAL(虚拟物品),POST(平邮),EMS(EMS),EXPRESS(其他快递公司)</param>///<paramname=”logistics_fee”>物流费用,默认为0</param>///<paramname=”logistics_payment”>物流支付类型:SELLER_PAY(卖家支付物流费用,费用不计到总价内),BUYER_PAY(买家支付物流费用,费用需要计到总价内),BUYER_PAY_AFTER_RECEIVE(买家收到货后直接支付给物流公司,费用不用计到总价中)</param>publicLogisticsInfo(stringlogistics_type,decimallogistics_fee,stringlogistics_payment){this._logistics_type=logistics_type;this._logistics_fee=logistics_fee;this._logistics_payment=logistics_payment;}///<summary>///物流类型///Alipay文档类型:string///说明:VIRTUAL:虚拟物品POST:平邮EMS:EMSEXPRESS:其他快递公司///</summary>publicstringLogistics_Type{get{return_logistics_type;}set{if(value!=null&&value.Length>50){thrownewArgumentOutOfRangeException(“无效的LogisticsType(物流类型)”,value,value.ToString());}_logistics_type=value;}}///<summary>///物流费用///Alipay文档类型:Number(8,2)///0.00–10000000.00默认为0///</summary>publicdecimalLogistics_Fee{get{_logistics_fee=decimal.Round(_logistics_fee,2);return_logistics_fee;}set{if(value<0.00m||value>100000000.00m){thrownewArgumentNullException(_logistics_fee.ToString(),”Price(商品单价)必须为0.01在100000000.00之间”);}_logistics_fee=value;}}///<summary>///物流支付类型///Alipay文档类型:string///说明:SELLER_PAY卖家支付(卖家支付物流费用,费用不计到总价内)///BUYER_PAY买家支付(买家支付物流费用,费用需要计到总价内)///BUYER_PAY_AFTER_RECEIVE货到付款(买家收到货后直接支付给物流公司,费用不用计到总价中)///</summary>publicstringLogistics_Payment{get{return_logistics_payment;}set{if(value!=null&&value.Length>50)thrownewArgumentOutOfRangeException(“无效的LogisticsPayment(物流支付类型)”,value,value.ToString());_logistics_payment=value;}}}这样,我们在相应的实物交易类中定义这样一个属性来支持多种物流方式的绑定了(详见NormalTrade类):

///<summary>///物流信息集合属性,详见<seecref=”Discuz.Payment.Alipay.GoodsTradeInfo.LogisticsInfo”/>类///</summary>publicLogisticsInfo[]Logistics_Info{get{return_logistics_info;}set{if(value!=null&&value.Length<=0){thrownewArgumentOutOfRangeException(“无效的物流信息(收货人姓名)”,value,value.ToString());}_logistics_info=value;}}当然在DigitalTrade类的公有构造函数中对交易配置类(config文件)进行了相应的初始化,以实现加载合作伙伴认证码及相应的Sign的属性,其代码如下所示:

publicDigitalTrade(){     TradeConfigInfotradeConfigInfo=TradeConfigs.GetConfig();_input_charset=tradeConfigInfo.Alipayconfiginfo.Inputcharset;_partner=tradeConfigInfo.Alipayconfiginfo.Partner;_sign=tradeConfigInfo.Alipayconfiginfo.Sign;TradeConfigs.SaveConfig(tradeConfigInfo);} 而有关配置信息类的说明文档详见:discuzNT 商品交易插件设计之用例模型

  当然具备了这些之后,我们还有必要进一步了解CreateDigitalGoodsTradeUrl及Create

NormalGoodsTradeUrl方法的一些内部信息,因为这两个方法便是我们按支付宝要求创建交易

url链接串的方法,下面以CreateDigitalGoodsTradeUrl为例来说明一下它的工作原理,首先我

们先看一下它的代码:

///<summary>///构造虚拟商品url///</summary>///<paramname=”digitalGoods”>虚拟商品信息</param>///<paramname=”key”>账户的交易安全校验码(key)</param>///<returns></returns>publicstringCreateDigitalGoodsTradeUrl(ITrade_goods){DigitalTradedigitalGoods=(DigitalTrade)_goods;stringtradeUrl=””;//未进行UrlEncode编码的链接参数串stringencodeUrl=””;//进行UrlEncode编码的链接参数串string[]urlParamArray=GetUrlParam(digitalGoods);//排序参数QuickSort(urlParamArray,0,urlParamArray.Length-1);tradeUrl=CreateTradeUrl(urlParamArray);encodeUrl=CreateEncodeUrl(urlParamArray);returnPayUrl+encodeUrl+string.Format(“&sign={0}&sign_type={1}”,GetMD5(tradeUrl+digitalGoods.Sign,digitalGoods.Input_Charset),digitalGoods.Sign_Type);}

   在这里,该方法将传入的交易信息(ITrade接口类型)转换成为DigitalTrade类型,然

后通过反射的方式将其属性转换成指定格式的数组,而这个工作交给了GetUrlParam方法。

其代码段如下所示:

///<summary>///反射出指定对象实例的所有属性值///</summary>///<paramname=”obj”>指定对象实例</param>///<returns></returns>privatestaticstring[]GetUrlParam(objectobj){PropertyInfo[]propertyInfos=obj.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance);stringurlParam=””;foreach(PropertyInfopiinpropertyInfos){if(pi.GetValue(obj,null)!=null){if(pi.Name==”Sign”||pi.Name==”Sign_Type”){continue;}//物流信息时if(pi.Name==”Logistics_Info”){LogisticsInfo[]logisticsInfoArray=((NormalTrade)obj).Logistics_Info;inti=0;foreach(LogisticsInfologisticsInfoinlogisticsInfoArray){if(logisticsInfo.Logistics_Type!=””){//物流参数的下标(第一种物流方式为””,第二种为”_1″,第三种为”_2″,以此类推)stringorderflag=””;if(i>0){orderflag=”_”+i;}urlParam+=”logistics_type”+orderflag+”=”+logisticsInfo.Logistics_Type+”&logistics_fee”+orderflag+”=”+logisticsInfo.Logistics_Fee+”&logistics_payment”+orderflag+”=”+logisticsInfo.Logistics_Payment+”&”;i++;}}}else{urlParam+=pi.Name.ToLower().Replace(“input_charset”,”_input_charset”)+”=”+pi.GetValue(obj,null).ToString()+”&”;}}}if(urlParam.EndsWith(“&”)){urlParam=urlParam.Substring(0,urlParam.Length-1);}returnurlParam.Split(‘&’);}在获得了相应的字符串数组信息之后,通过一个快速排序方法将这个字符串数组进行排序,即:QuickSort方法。当然在支付宝官方所给的示例子使用了冒泡算法,虽然能实现功能但效率上实在是不敢恭维。快速算法的实现代码如下所示:

#region(URL)参数快速排序///<summary>///把数组划分为两个部分///</summary>///<paramname=”arr”>划分的数组</param>///<paramname=”low”>数组低端上标</param>///<paramname=”high”>数组高端下标</param>///<returns></returns>publicstaticintPartition(string[]strArray,intlow,inthigh){//进行一趟快速排序,返回中心轴记录位置//arr[0]=arr[low];stringpivot=strArray[low];//把中心轴置于arr[0]while(low<high){while(low<high&&System.String.CompareOrdinal(strArray[high],pivot)>=0)–high;//将比中心轴记录小的移到低端Swap(refstrArray[high],refstrArray[low]);while(low<high&&System.String.CompareOrdinal(strArray[low],pivot)<=0)++low;Swap(refstrArray[high],refstrArray[low]);//将比中心轴记录大的移到高端}strArray[low]=pivot;//中心轴移到正确位置returnlow;//返回中心轴位置}publicstaticvoidSwap(refstringi,refstringj){stringt;t=i;i=j;j=t;}///<summary>///快速排序算法///</summary>///<paramname=”arr”>划分的数组</param>///<paramname=”low”>数组低端上标</param>///<paramname=”high”>数组高端下标</param>publicstaticvoidQuickSort(string[]strArray,intlow,inthigh){if(low<=high-1)//当arr[low,high]为空或只一个记录无需排序{intpivot=Partition(strArray,low,high);//左子树QuickSort(strArray,low,pivot-1);//右子树QuickSort(strArray,pivot+1,high);}}#endregion

  而排序结序后,通过CreateTradeUrl方法将这个经过排序的字符串数组组合成为我们所需要

的链接串格式,如下所示:

 tradeUrl = CreateTradeUrl(urlParamArray);

  接着调用CreateEncodeUrl方法再次对经过排序的字符串数组进行编码并也转换成所需要的

链接串格式,当然这里的链接串将会是我们在IE地址栏中所出现的那个链接串中的内容。而上面

CreateTradeUrl的链接串则只是为了进行GetMD5加密并获得 sign签名才做的,这部分内容请

参见下面代码段:

encodeUrl=CreateEncodeUrl(urlParamArray);returnPayUrl+encodeUrl+string.Format(“&sign={0}&sign_type={1}”,GetMD5(tradeUrl+digitalGoods.Sign,digitalGoods.Input_Charset),digitalGoods.Sign_Type);这样我们就可以通过传入交易信息来获取支付宝交易URL链接串了。好了,今天的内容就先到这里,在下一篇文章中,我们将回到在线支付的业务流程中,继续了解在线支付流程中的支付宝数据回传以及在线进行商品交易时的流程设计方面的内容。tag:alipay,支付宝,discuznt,online trade,在线交易 作者:代震军,daizhj原文链接:http://www.cnblogs.com/daizhj/archive/2008/08/18/1270189.html切忌贪婪,恨不得一次玩遍所有传说中的好景点,

DiscuzNT 商品交易插件设计之[线上交易]—支付宝

相关文章:

你感兴趣的文章:

标签云: