在 ASP.NET 中,使用贝宝支付系统

分享于 

48分钟阅读

Web开发

  繁體
Screenshot - Use_of_the_PayPal_payment_system_in_ASPNET.gif

介绍

商业站点的人面临着这个问题,这是世界上最流行的支付系统之一,贝宝。 这个系统经常被选择,因为它是可以靠的,易于使用,并且允许帐户轻松打开。 要打开帐户,你只需在美国银行有一张信用卡和/或者帐户。 系统的缺陷之一是它的安全策略。 然而,实践 evinces,如果你认真遵循系统规则,那么错误是非常少见的。 本文旨在展示如何组织付款处理以支持可靠性和安全性。 本文还针对你提供了一个简单的在线商店开发示例,以展示与bayes系统的交互。 你可以使用应用程序中的代码来组织与PayPal系统的交互,并处理付款。

本文重点介绍了利用 IPN ( 即时付款通知) 进行自动支付验证的过程。 本文基于KB_Soft集团的经验和官方PayPal文档。

PayPal付款类型

PayPal支持几种类型的付款:

  • PayPal购物车中商品的付款。 PayPal负责在这个例子中支持购物车的所有操作。 然而,该选项不提供实现某些项目所需的最大灵活性,因此本文不考虑这里选项。
  • "一点点"购物。在这种情况下,货物不会放入购物车。 这种方法也用来支付购物车中没有PayPal的商品的支付。 这就是为什么这个选项提供了最大的灵活性和对购物车的完全控制。
  • 定期付费或者订阅。贝贝提供订阅能力,这意味着定期从用户帐户中转移。 用户随时可以退订。 卖方可以指定订购期限和费用。 他还可以组织试用期,让用户评估他提供的服务的质量。 试用期可以是付费的,也可以是免费的。

出于描述 上面的原因,本文将考虑第二个选项。 将不描述订阅,以保持示例简单。 为了与PayPal系统交互,KB_Soft组使用 UserControl,它是内部开发的用于这些目的的产品。 为与PayPal一起工作的代码提供一个特殊的HTML表单,以便明确解释与系统的交互。 贝宝也提供自己的控制作为动态库,但不幸的是,这个控件只能用美国语言环境( en )。 此外,它还没有提供与PayPal一起工作的全部功能。 它也不提供处理某些项目所需的灵活性。

付款过程

付款过程非常简单。 创建一个包含有关项( 标识符,NAME 和成本) 和按钮发送表单的信息的一组隐藏字段。 应该注意,所有价格都应该在point后用两个数字表示。 如果项目成本为 $10,则它的价格应表示为" 10.00"。 发送表单时,买家转到 网站并完成付款过程。 当使用真正的贝宝帐户时,表单应该在这里发送( )。 这个开发的例子还允许你使用PayPal沙箱。 使用 UseSandbox的参数,并且表单将在这里发送

点击"shopping

最简单形式的代码:

<formmethod="post"action="https://www.paypal.com/cgi-bin/webscr"><inputtype="hidden"name="cmd"value="_xclick"><inputtype="hidden"name="business"value="my@email.com"><inputtype="hidden"name="item_name"value="Item name"><inputtype="hidden"name="item_number"value="1234"><inputtype="hidden"name="amount"value="19.95"><inputtype="hidden"name="no_shipping"value="1"><inputtype="submit"value="Buy Now"></form>
主要参数的描述
参数描述
cmd必须具有参数。它必须具有未加密请求的_xclick 值。
business参数是必需的,代表卖方的E-mail。
item_number这里参数是项标识符。 这里值不会显示给用户;但是,它将在事务确认时传递给你的脚本。 如果你打算使用PayPal支付购物车中的商品,那么你可以在这个参数中传递cart的标识符。
item_name这是将向用户显示的项的NAME。
no_shipping提示客户发送地址。
默认或者 0: 提示客户包括送货地址。
1: 客户不要求提供送货地址。
2: 客户必须提供送货地址。
return这是在成功执行付款后用户将被重定向的URL。 如果未通过这里参数,则买方仍在PayPal网站上。
rm此参数确定成功事务的信息将传递给返回参数中指定的脚本的方式。 "1 that表示不传递任何参数。 "2 that表示 POST 方法将被使用。 "0 that表示 GET 方法将被使用。 默认情况下参数为" 0"。
cancel_return这是当用户取消付款时用户将被重定向的URL。 如果参数未通过,则买方仍在PayPal网站上。
notify_url这是PayPal将传递关于事务( IPN )的信息的URL。 如果未传递参数,则将使用帐户设置中的值。 如果不能在帐户设置中定义这里值,则将不使用 IPN。
custom这个字段不会在购物过程中进行,它将在事务确认时简单地传递给openoffice脚本。
invoice这里参数用于传递发票号。 参数不是必需的,但传递给它的参数对于每个事务都必须是唯一的。
amount这里参数表示支付金额。 如果未传递参数,则允许用户输入( 这是用来捐赠的)。
currency_code这里参数表示货币代码。 可能的值是" USD"," EUR"," GBP"," YEN"," CAD"等等 它是" USD"。
表 1 - 请求表单的主要参数。

表 1列出了最常用的参数。 有关完整参数的完整 List的参考信息,请参阅本文末尾的参考文档。

IPN

IPN ( 即时付款通知) 是一种允许自动付款处理的PayPal技术。 这项技术的本质在于在卖方的服务器上创建了一个特殊的脚本。 例如发生与卖家--有关的事件时,付款转移,付款取消,创建或者取消,等等 --将使用事务信息向IPN脚本发送事务请求。 脚本将向PayPal服务器发送一个请求,以验证事务。

所以买家执行了付款。 超过几秒钟后,bayes服务器将请求发送到在帐户设置中指定或者传递的notify_url 参数。 一个好的IPN脚本是支付安全的关键。 如果你曾经听说过使用ruby的卖家是某人的欺骗者,那么请确保这些卖家不使用互联网。

首先,脚本必须确保它是由PayPal服务器调用的。 为此,脚本在这里生成一个 POST 请求到 ,这里是 它传递接收的所有变量,没有任何更改,以及具有 _notify-validate 值的cmd 参数。 作为对请求的响应,脚本接收到 VERIFIED --意味着事务被成功验证了--或者 INVALID。 如果脚本接收到 INVALID,则必须终止。

然后,脚本必须检查付款收件人,因为潜在入侵者可能会改变付款的形式。 付款收件人由 businessreceiver_email 变量决定。 由于PayPal允许多个电子邮件注册一个帐户,所以需要两个变量。 在创建帐户期间指定的E-mail 是主要的。 receiver_email 始终是主 E-mail。 如果向其他 E-mail 发送付款,则将 E-mail 传递到 business 参数中。 如果 business 和/或者 receiver_email 不包含预期值,则脚本立即终止。

然后该脚本必须检查付款的金额和货币。 由于潜在入侵者可能会更改表单中的付款金额,因此需要进行这里验证。 在用例订阅中,脚本应检查所有订阅参数,换句话说,的参数设置。持续期和成本。

同一事务的IPN可以发送多次。 例如,如果某种原因延迟付款,第一个网络互联网将在付款后立即发送。 执行付款或者取消付款后,将发送第二个 IPN。 如果网络连接脚本不返回等于 200的HTTP状态,那么das在一段时间后再次发送网络。 在 10秒内重复,然后在 20秒,如果需要,等等,达 24小时。 如果脚本在 4天内没有响应,则贝宝停止发送 IPN。 这可以用于在IPN脚本中发生错误时不释放事务信息。 例如,如果脚本无法连接到存储事务信息的数据库,则脚本可以返回等于 500的HTTP状态。 如果IPN脚本不引用PayPal服务器来验证事务,则重复的IPN将以同样的方式发送。

returnrmnotify_url 参数的描述可以看出,IPN可以传递给 returnnotify_url 参数中指定的两个脚本。 它们之间有两个区别:

  • 在执行付款后,return的IPN将只发送一次。 notify_url 可以多次调用(。查看 上面 段落)。
  • return 脚本的结果将显示给用户。 应该注意,如果结果包含链接,则链接必须是绝对的。 浏览器中不显示 notify_url 脚本的结果

接收的POST 变量包含事务信息。 使用范围最广泛的变量如下:

参数描述
txn_id唯一交易编号。
payment_date" 18: 30: 30 Jan,2000 PST"格式的付款日期。
payer_email买方 E-mail。
business卖方 E-mail。
payer_id买方的唯一标识符。 由贝贝的帮助执行付款的那些人是由 E-mail 地址标识的。 但是,考虑到更改 E-mail的可以能性,payer_id 应该更好地用于识别买方。
item_number项目标识符。
item_name项目名称。
txn_type事务类型可能的值有:
web_accept - 通过单击"立即购买"按钮来执行付款。
cart - 付款是使用内置的PayPal购物车进行的。
send_money - 使用"发送货币"函数执行付款。
reversal - 根据他的计划将钱返还给买家。
payment_status付款状态可能的值有:
Completed - 交易已经成功执行,资金被转移到卖家帐户。 如果 txn_type = reversal,则将资金返回到买方帐户。
Pending - 付款延迟。 延迟原因是在 pending_reason 变量中确定的。 付款完成后,PayPal将发送另一个通知。
Failed - 付款失败只有当从银行帐户执行付款时,才能使用这里状态。
Denied - 卖家取消了付款。 付款在卖方取消具有 Pending 状态的付款时处于这里状态。
Refunded - 将钱返回给买家。 付款在卖方取消具有 Completed 状态的付款时处于这里状态。
pending_reason付款延迟的原因可能的值是:
echeck - 使用支票进行付款
multi_currency - 在卖方帐户设置中指定的货币中执行付款。 付款将在卖方确认交易时完成。
intl - 卖家不是美国居民。 付款将在卖方确认交易时完成。
verify - 卖方的帐户处于 unverified 状态。 付款将在卖方确认时完成。
address - 卖方帐户的设置要求买方指定传递地址,但买方不指定地址。 付款将在卖方确认交易后完成。
upgrade - 使用信用卡执行付款,卖方的帐户为 Personal 状态。 若要完成付款,卖方应将帐户升级到 business 或者 Premier
unilateral - 卖方的E-mail 不能在系统中注册。
other - 另一个原因卖家需要联系支持以了解更多关于。
payment_type付款类型可能的值有:
echeck - 使用支票进行付款。
instant - 付款是用信用卡或者使用银行帐户的银行帐户或者钱包来执行的。
mc_gross付款金额。
mc_fee佣金金额。卖方账户上的金额被确定为 mc_gross - mc_fee
mc_currency付款币种。
first_name买方姓名。
last_name最后一个买家名称。
address_street街道。
address_city城市。
address_state状态/区域。
address_zip邮编。
address_country国家。
verify_sign数字签名。用于交易验证中的PayPal。
表 2 - 最广泛使用的变量。

IPN加工的一个例子

给定 below 是使用 PayPal IPN的脚本的一个例子。 我们发布这个脚本不为你提供可以复制/粘贴的准备脚本,但是说明了使用dtmf的一般原则。 KB_Soft组使用更复杂的脚本创建使用PayPal系统的站点。 这个脚本相当简单,但同时也说明了IPN处理的主要原理。

给定代码创建一个在线商店的简化版本。 买家向购物车中添加商品并支付费用。 付款后,将创建付款报告。

关于商品和购物车内容的所有信息都存储在XML文件中。 我们选择这种信息存储方式的原因是兼容性的原因。 下载代码的任何用户都可以轻松调整和测试已经创建的在线商店。 对于实际应用,数据库应该更好地用于存储关于商品。购物车。支付请求和响应的信息。

我们使用 Goods.xml 文件存储有关商品的信息,它的结构如下所示:

<Goods><Goodid="0"name="Sample of good"price="10.99"/></Goods>

那。

  • id 是唯一的项标识符
  • name 是项名称
  • price 是物料价格

为了简化这个例子,我们没有提供创建具有允许新项目添加到商品目录中的功能的在线商店。 可以根据需要手动将有关新项的信息添加到XML文件中。

我们使用 Carts.xml 文件存储有关购物车的信息,它的结构如下所示:

<Carts><Cartrec_id="0"cart_id="1"item_id="0"price="10.99"quantity="1"/></Carts>

那。

  • rec_id 是唯一的记录标识符
  • cart_id 是包含这里项的购物车的标识符
  • id 是项标识符
  • price 是物料价格
  • quantity 是有序项的数量

对于实际的在线商店,有关付费车的信息并不存储在那里,但是被写入了数据库的table。 为了简化流程,让你轻松跟踪支付结果,我们在给定的代码中没有实现这个功能。 这里外,真正的网上商店应该 register 用户,以便能够识别和创建只能访问他们的用户。 示例不使用注册,因此它不控制不同用户对购物车的访问。 由于用户只需要选择它的购物车标识符,这里过程就简化了。

我们使用下面的结构来存储有关付款请求的信息,该文件使用了 PaymentRequests.xml 文件:

<Requests><Requestrequest_id="0"cart_id="1"price="10.99"request_date="5/28/2007 1:15:18 PM"/></Requests>

那。

  • request_id 是唯一的请求标识符
  • cart_id 是支付的购物车的标识符
  • price 是商品的成本
  • request_date 是创建请求的日期和时间

在实际在线商店中,关于付款请求的信息包含付款细节的table的付款细节标识。 为了简化代码,我们没有使用这个。

我们使用以下结构来存储关于付款请求响应的信息,该请求使用了 PaymentResponses.xml:

<Responses><Responsepayment_id="0"txn_id="3PP58082BD3079037"payment_date="5/28/2007 1:22:40 PM"payment_price="10.99"email= my@email.comfirst_name=""last_name=""street=""city=""state=""zip=""country=""request_id="0"is_success="True"reason_fault=""/></Responses>

那。

  • payment_id 是唯一的付款标识符
  • txn_id 是PayPal事务的唯一编号
  • payment_date 是执行付款的日期和时间
  • payment_price 是付款金额
  • email 是买方的E-mail
  • first_name 是第一个采购员名称
  • last_name 是采购员的最后一个名字
  • street 是买家的街道
  • city 是 buyer
  • state 是买方的状态
  • zip 是采购员代码的邮政编码
  • country 是买方所在的国家/地区
  • request_id 是付款请求的标识符
  • is_success 表示是否已经成功执行付款
  • reason_fault 是付款失败的可能原因

如果我们使用XML文件存储信息,我们最好使用 XML Schema 来验证信息。 但是,为了简化示例,我们不执行验证。

发送到PayPal的付款请求的形式如下:

<formid="payForm"method="post"action="<%Response.Write (URL)%>"><inputtype="hidden"name="cmd"value="<%Response.Write (cmd)%>"><inputtype="hidden"name="business"value="<%Response.Write (business)%>"><inputtype="hidden"name="item_name"value="<%Response.Write (item_name)%>"><inputtype="hidden"name="amount"value="<%Response.Write (amount)%>"><inputtype="hidden"name="no_shipping"value="<%Response.Write (no_shipping)%>"><inputtype="hidden"name="return"value="<%Response.Write (return_url)%>"><inputtype="hidden"name="rm"value="<%Response.Write (rm)%>"><inputtype="hidden"name="notify_url"value="<%Response.Write (notify_url)%>"><inputtype="hidden"name="cancel_return"value="<%Response.Write (cancel_url)%>"><inputtype="hidden"name="currency_code"value="<%Response.Write (currency_code)%>"><inputtype="hidden"name="custom"value="<%Response.Write (request_id)%>"></form>

那。

  • 根据沙箱或者实际PayPal帐户是否应该使用,URL 是要使用的URL
  • cmd 是一个发送给PayPal的命令
  • business 是卖方的E-mail
  • item_name 是一个项目 NAME -- 换句话说,买方为--付款,将显示给用户;
  • amount 是付款金额
  • no_shipping 是确定是否应请求送达地址的参数
  • return_url 是成功执行付款时买方将被重定向到的URL
  • rm 是一个参数,它决定成功完成事务的信息将发送到返回参数中指定的脚本的方式。
  • notify_url 是 URL PayPal将发送有关事务( IPN )的信息
  • cancel_url 是取消付款时买方重定向到的URL
  • currency_code 是货币代码
  • request_id 是付款请求的标识符

变量的值是在 PayPal.aspx.cs 或者附加到本文的源代码的tPayPal.aspx.vb 文件中设置的。 有关表单字段的更详细描述,请参见 table 1.

request_id 传递给 custom 字段时,它允许IPN脚本恢复有关cart的信息。 如果买方取消付款,他将被重定向到 cancel_url。 但是,如果他执行支付,他将被重定向到 return_url。 在后一种情况下,我们可以测试与贝宝的交互,检查支付是否执行,创建支付报告并感谢买家购买。 在 given payment_success.aspx.cs 或者 payment_success.aspx.vb 文件中使用IPN处理代码,因为实际产品应该验证 notify_url 参数中的付款,以达到安全性的目的。 为了使测试过程尽可能地提供信息,payment_success.aspx.cs 或者 payment_success.aspx.vb 代码被专门编写。 代码包含只有在测试阶段才重要的消息。 这里信息被写入日志文件。 文件不仅存储关键错误,还存储允许站点继续工作的错误。

通常,应该正确处理错误消息,但不能将它的作为异常显示给用户。 Response.Write()的构造不是一个好主意。 实际站点通常创建一个特殊页面,其中发送有关错误的信息。 然后格式化信息并向用户显示。 例如,如果出现异常或者从站点请求的页面不存在,应该重定向到这里页面。 为了简化给定的示例,代码将写入有关日志文件中发生的大多数错误的信息。

返回参数是有用的,因为在执行付款时,它允许验证的结果显示给用户。 但是,验证并没有提供 100%保证付款真正放入卖方的帐户中。 例如,如果买方使用电子支票,则只有在支票处于银行的帐户后才能保证钱入帐。 这就是为什么真正的网上商店应该使用互联网和付款,检查支付和协议在网络的代码。 这里外,应在发送之前加密表单内容,以避免伪造付款信息。 这就意味着被称为加密的网站支付。 如果你不要使用加密的网站付款( EWP ) 验证,必须检查价格to交易to和to发送的它的他数据。 通过检查数据,你可以确保没有被欺骗。

KB_Soft组在它的项目中使用从PayPal接收的参数的EWP和验证。 这提供了重复验证检查并排除了任何信息伪造可能的。 为了简化在线商店的给定示例,并使它的在任何ruby帐户上工作,我们只使用网络互联网。 因为我们使用 EWP,我们必须创建 private 和 public 密钥,并在PayPal服务器上将创建的public 密钥上传到我们的帐户。 然后我们需要使用获得的证书标识符来加密请求的形式。 此外,要使用 EWP,我们需要下载PayPal系统本身的public 键。 本文不是针对使用EPW来详细描述工作原理的,因此你可以访问PayPal站点来查找关于这个问题的详细信息。

IPNHandler 类的Page_Load 过程代码是 below。 你可以在与本文附加的源代码关联的档案中找到详细信息。

C#

privatevoid Page_Load(object sender, EventArgs e)
 {
 string requestUriString;
 CultureInfo provider = new CultureInfo("en-us");
 string requestsFile = this.Server.MapPath(
 "~/App_Data/PaymentRequests.xml");
 requests.Clear();
 if (System.IO.File.Exists(requestsFile))
 {
 requests.ReadXml(requestsFile);
 }
 else {
 Carts.CreateXml(requestsFile, "Requests");
 requests.ReadXml(requestsFile);
 }
 string responseFile = this.Server.MapPath(
 "~/App_Data/PaymentResponses.xml");
 responses.Clear();
 if (System.IO.File.Exists(responseFile))
 {
 responses.ReadXml(responseFile);
 }
 else {
 Carts.CreateXml(responseFile, "Responses");
 responses.ReadXml(responseFile);
 }
 string strFormValues = Encoding.ASCII.GetString(
 this.Request.BinaryRead(this.Request.ContentLength));
 // getting the URL to work withif (String.Compare(
 ConfigurationManager.AppSettings["UseSandbox"].ToString(),
 "true", false) == 0)
 {
 requestUriString =
 "https://www.sandbox.paypal.com/cgi-bin/webscr";
 }
 else {
 requestUriString = "https://www.paypal.com/cgi-bin/webscr";
 }
 // Create the request back HttpWebRequest request =
 (HttpWebRequest)WebRequest.Create(requestUriString);
 // Set values for the request back request.Method = "POST";
 request.ContentType = "application/x-www-form-urlencoded";
 string obj2 = strFormValues + "&cmd=_notify-validate";
 request.ContentLength = obj2.Length;
 // Write the request back IPN strings StreamWriter writer =
 new StreamWriter(request.GetRequestStream(), Encoding.ASCII);
 writer.Write(RuntimeHelpers.GetObjectValue(obj2));
 writer.Close();
 //send the request, read the response HttpWebResponse response = (HttpWebResponse)request.GetResponse();
 Stream responseStream = response.GetResponseStream();
 Encoding encoding = Encoding.GetEncoding("utf-8");
 StreamReader reader = new StreamReader(responseStream, encoding);
 // Reads 256 characters at a time. char[] buffer = new char[0x101];
 int length = reader.Read(buffer, 0, 0x100);
 while (length >0)
 {
 // Dumps the 256 characters to a stringstring requestPrice;
 string IPNResponse = newstring(buffer, 0, length);
 length = reader.Read(buffer, 0, 0x100);
 try {
 // getting the total cost of the goods in// cart for an identifier// of the request stored in the"custom" variable requestPrice =
 GetRequestPrice(this.Request["custom"].ToString());
 if (String.Compare(requestPrice, "", false) == 0)
 {
 Carts.WriteFile("Error in IPNHandler: amount =");
 reader.Close();
 response.Close();
 return;
 }
 }
 catch (Exception exception)
 {
 Carts.WriteFile("Error in IPNHandler: " + exception.Message);
 reader.Close();
 response.Close();
 return;
 }
 NumberFormatInfo info2 = new NumberFormatInfo();
 info2.NumberDecimalSeparator =".";
 info2.NumberGroupSeparator =",";
 info2.NumberGroupSizes = new int[] { 3 };
//if the request is verified
 if (String.Compare(IPNResponse,"VERIFIED", false) == 0)
 {
//check the receiver's e-mail (login is user's
//identifier in PayPal)
//and the transaction type
 if ((String.Compare(this.Request["receiver_email"],
 this.business, false)!= 0) ||
 (String.Compare(this.Request["txn_type"],
"web_accept", false)!= 0))
 {
 try
 {
//parameters are not correct. Write a
//response from PayPal
//and create a record in the Log file.
 this.CreatePaymentResponses(this.Request["txn_id"],
 Convert.ToDecimal(
 this.Request["mc_gross"], info2),
 this.Request["payer_email"],
 this.Request["first_name"],
 this.Request["last_name"],
 this.Request["address_street"],
 this.Request["address_city"],
 this.Request["address_state"],
 this.Request["address_zip"],
 this.Request["address_country"],
 Convert.ToInt32(this.Request["custom"]), false,
"INVALID payment's parameters" +
"(receiver_email or txn_type)");
 Carts.WriteFile(
"Error in IPNHandler: INVALID payment's" +
" parameters(receiver_email or txn_type)");
 }
 catch (Exception exception)
 {
 Carts.WriteFile("Error in IPNHandler: " +
 exception.Message);
 }
 reader.Close();
 response.Close();
 return;
 }
//check whether this request was performed
//earlier for its identifier
 if (this.IsDuplicateID(this.Request["txn_id"]))
 {
//the current request is processed. Write
//a response from PayPal
//and create a record in the Log file.
 this.CreatePaymentResponses(this.Request["txn_id"],
 Convert.ToDecimal(this.Request["mc_gross"], info2),
 this.Request["payer_email"],
 this.Request["first_name"],
 this.Request["last_name"],
 this.Request["address_street"],
 this.Request["address_city"],
 this.Request["address_state"],
 this.Request["address_zip"],
 this.Request["address_country"],
 Convert.ToInt32(this.Request["custom"]), false,
"Duplicate txn_id found");
 Carts.WriteFile(
"Error in IPNHandler: Duplicate txn_id found");
 reader.Close();
 response.Close();
 return;
 }
//the amount of payment, the status of the
//payment, and a possible reason of delay
//The fact that Getting txn_type=web_accept or
//txn_type=subscr_payment are got odes not mean that
//seller will receive the payment.
//That's why we check payment_status=completed. The
//single exception is when the seller's account in
//not American and pending_reason=intl
 if (((String.Compare(
 this.Request["mc_gross"].ToString(provider),
 requestPrice, false)!= 0) ||
 (String.Compare(this.Request["mc_currency"],
 this.currency_code, false)!= 0)) ||
 ((String.Compare(this.Request["payment_status"],
"Completed", false)!= 0) &&
 (String.Compare(this.Request["pending_reason"],
"intl", false)!= 0)))
 {
//parameters are incorrect or the payment
//was delayed. A response from PayPal should not be
//written to DB of an XML file
//because it may lead to a failure of
//uniqueness check of the request identifier.
//Create a record in the Log file with information
//about the request.
 Carts.WriteFile(
"Error in IPNHandler: INVALID payment's parameters."+
"Request:" + strFormValues);
 reader.Close();
 response.Close();
 return;
 }
 try
 {
//write a response from PayPal
 this.CreatePaymentResponses(this.Request["txn_id"],
 Convert.ToDecimal(this.Request["mc_gross"], info2),
 this.Request["payer_email"],
 this.Request["first_name"],
 this.Request["last_name"],
 this.Request["address_street"],
 this.Request["address_city"],
 this.Request["address_state"],
 this.Request["address_zip"],
 this.Request["address_country"],
 Convert.ToInt32(this.Request["custom"]), true,"");
 Carts.WriteFile(
"Success in IPNHandler: PaymentResponses created");
///////////////////////////////////////////////////
//Here we notify the person responsible for
//goods delivery that
//the payment was performed and providing
//him with all needed information about
//the payment. Some flags informing that
//user paid for a services can be also set here.
//For example, if user paid for registration
//on the site, then the flag should be set
//allowing the user who paid to access the site
//////////////////////////////////////////////////
 }
 catch (Exception exception)
 {
 Carts.WriteFile(
"Error in IPNHandler:" + exception.Message);
 }
 }
 else
 {
 Carts.WriteFile(
"Error in IPNHandler. IPNResponse = 'INVALID'");
 }
 }
 reader.Close();
 response.Close();
 }

Visual Basic

PrivateSub Page_Load(ByVal sender As System.Object,
 ByVal e As System.EventArgs) HandlesMyBase.Load
 Dim ci As CultureInfo = New CultureInfo("en-us")
 Dim requestsFile AsString = Server.MapPath(
 "~/App_Data/PaymentRequests.xml")
 requests.Clear()
 If File.Exists(requestsFile) Then requests.ReadXml(requestsFile)
 Else KBSoft.Carts.CreateXml(requestsFile, "Requests")
 requests.ReadXml(requestsFile)
 EndIfDim responseFile AsString = Server.MapPath(
 "~/App_Data/PaymentResponses.xml")
 responses.Clear()
 If File.Exists(responseFile) Then responses.ReadXml(responseFile)
 Else KBSoft.Carts.CreateXml(responseFile, "Responses")
 responses.ReadXml(responseFile)
 EndIfDim strFormValues AsString = Encoding.ASCII.GetString(
 Request.BinaryRead(Request.ContentLength))
 Dim strNewValue
 ' getting the URL to work withDim URL AsStringIf AppSettings("UseSandbox").ToString = "true"Then URL = "https://www.sandbox.paypal.com/cgi-bin/webscr"Else URL = "https://www.paypal.com/cgi-bin/webscr"EndIf' Create the request backDim req As HttpWebRequest = CType(WebRequest.Create(URL),
 HttpWebRequest)
 ' Set values for the request back req.Method = "POST" req.ContentType = "application/x-www-form-urlencoded" strNewValue = strFormValues + "&cmd=_notify-validate" req.ContentLength = strNewValue.Length
 ' Write the request back IPN stringsDim stOut As StreamWriter = New StreamWriter(
 req.GetRequestStream(), _
 Encoding.ASCII)
 stOut.Write(strNewValue)
 stOut.Close()
 'send the request, read the responseDim strResponse As HttpWebResponse = CType(req.GetResponse(),
 HttpWebResponse)
 Dim IPNResponseStream As Stream = strResponse.GetResponseStream
 Dim encode As Encoding = System.Text.Encoding.GetEncoding("utf-8")
 Dim readStream AsNew StreamReader(IPNResponseStream, encode)
 Dim read(256) As [Char]
 ' Reads 256 characters at a time.Dim count AsInteger = readStream.Read(read, 0, 256)
 While count> 0' Dumps the 256 characters to a stringDim IPNResponse AsNew [String](read, 0, count)
 count = readStream.Read(read, 0, 256)
 Dim amount AsStringTry' getting the total cost of the goods in cart for an' identifier of the request stored in the"custom"' variable amount = GetRequestPrice(Request("custom").ToString)
 If amount = ""Then KBSoft.Carts.WriteFile("Error in IPNHandler: amount =""")
 readStream.Close()
 strResponse.Close()
 ReturnEndIfCatch ex As Exception
 KBSoft.Carts.WriteFile("Error in IPNHandler:" + ex.Message)
 readStream.Close()
 strResponse.Close()
 ReturnEndTryDim provider As NumberFormatInfo = New NumberFormatInfo()
 provider.NumberDecimalSeparator = "." provider.NumberGroupSeparator = "," provider.NumberGroupSizes = NewInteger() {3}
 ' if the request is verifiedIf IPNResponse = "VERIFIED"Then' check the receiver's e-mail (login is user's' identifier in PayPal) and the transaction typeIf Request("receiver_email") <> business Or Request(
 "txn_type") <> "web_accept"ThenTry' parameters are not correct. Write a response from' PayPal and create a record in the Log file. CreatePaymentResponses(Request("txn_id"),
 Convert.ToDecimal(Request("mc_gross"), provider), _
 Request("payer_email"), Request("first_name"),
 Request("last_name"), Request("address_street"), _
 Request("address_city"), Request("address_state"),
 Request("address_zip"), Request("address_country"), _
 Convert.ToInt32(Request("custom")), False,
 "INVALID payment's parameters (
 receiver_email or txn_type)")
 KBSoft.Carts.WriteFile(
 "Error in IPNHandler: INVALID payment's parameters"+
 " (receiver_email or txn_type)")
 Catch ex As Exception
 KBSoft.Carts.WriteFile(
 "Error in IPNHandler:" + ex.Message)
 EndTry readStream.Close()
 strResponse.Close()
 ReturnEndIf' check whether this request was performed earlier for its' identifierIf IsDuplicateID(Request("txn_id")) Then' the current request is processed. Write a response from' PayPal and create a record in the Log file. CreatePaymentResponses(Request("txn_id"),
 Convert.ToDecimal(Request("mc_gross"), provider), _
 Request("payer_email"), Request("first_name"),
 Request("last_name"), Request("address_street"), _
 Request("address_city"), Request("address_state"),
 Request("address_zip"), Request("address_country"), _
 Convert.ToInt32(Request("custom")), False,
 "Duplicate txn_id found")
 KBSoft.Carts.WriteFile(
 "Error in IPNHandler: Duplicate txn_id found")
 readStream.Close()
 strResponse.Close()
 ReturnEndIf' the amount of payment, the status of the payment, and a' possible reason of delay' The fact that Getting txn_type=web_accept or' txn_type=subscr_payment are got odes not mean that' seller will receive the payment.' That's why we check payment_status=completed. The' single exception is when the seller's account in' not American and pending_reason=intlIf Request("mc_gross").ToString(ci) <> amount Or Request(
 "mc_currency") <> currency_code Or _
 (Request("payment_status") <> "Completed"And Request(
 "pending_reason") <> "intl") Then' parameters are incorrect or the payment was delayed.' A response from PayPal should not be' written to DB of an XML file' because it may lead to a failure of uniqueness check of' the request identifier.' Create a record in the Log file with information about' the request. KBSoft.Carts.WriteFile(
 "Error in IPNHandler: INVALID payment's parameters."+
 " Request:" + strFormValues)
 readStream.Close()
 strResponse.Close()
 ReturnEndIfTry' write a response from PayPal CreatePaymentResponses(Request("txn_id"),
 Convert.ToDecimal(Request("mc_gross"), provider), _
 Request("payer_email"), Request("first_name"),
 Request("last_name"), Request("address_street"), _
 Request("address_city"), Request("address_state"),
 Request("address_zip"), Request("address_country"), _
 Convert.ToInt32(Request("custom")), True, "")
 KBSoft.Carts.WriteFile(
 "Success in IPNHandler: PaymentResponses created")
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Here we notify the person responsible for goods delivery' that the payment was performed and providing him with' all needed information about the payment. Some flags' informing that user paid for a services can be also' set here. For example, if user paid for registration' on the site, then the flag should be set' allowing the user who paid to access the site''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Catch ex As Exception
 KBSoft.Carts.WriteFile(
 "Error in IPNHandler:" + ex.Message)
 EndTryElse KBSoft.Carts.WriteFile(
 "Error in IPNHandler. IPNResponse = 'INVALID'")
 EndIfEndWhile readStream.Close()
 strResponse.Close()
 EndSub

Web.Config 调整参数

要使用本文附带的源代码,需要正确指定 web.config 参数。 在调整 web.config 文件中的参数时,应特别注意 appSettings 设置:

<appSettings><!-- PayPay parameters--><addkey="BusinessEmail"value="mymail@mail.com"/><addkey="CancelPurchaseUrl"value="http://YOUR_IP/paypal/default.aspx"/><addkey="ReturnUrl"value="http://YOUR_IP/paypal/payment_success.aspx"/><addkey="NotifyUrl"value="http://YOUR_IP/paypal/IPNHandler.aspx"/><addkey="CurrencyCode"value="USD"/><addkey="UseSandbox"value="true"/><addkey="SendToReturnURL"value="true"/></appSettings>

BusinessEmail 参数中指定付款收件人的E-mail。 这可以是在 E-mail 上创建帐户或者在帐户参数中指定的备用 E-mail 作为替代 E-mail 使用的。 用作PayPal帐户登录的E-mail 通常在这里参数中指定。 对于 CancelPurchaseUrlReturnUrlNotifyUrl 参数: 在 YOUR_IP 中,你需要指定你的全局IP地址 --,而不是在本地网络--中的IP地址。 测试时,你只需指定 localhost 而不是 YOUR_IP。 应该注意,只有在正确指定了全局IP地址或者已经注册域的NAME 时,IPN才会起作用。 如果指定 localhost,那么只有 ReturnUrl 参数中指定的脚本才能够支持与PayPal的交互。 在 NotifyUrl 参数中指定的脚本将不会。 你应该记住,IPN可能被阻塞,这取决于防火墙设置。

为"贝宝"虚拟目录提供了 上面 链接。 在 PayPal Business中输入地址时,需要指定它的名称,而不是"贝宝。",以检查 NotifyUrl 是否有效,在业务或者主帐户的Profile-> 付款通知中输入地址。 选中这里页面上的复选框并单击"保存"按钮。 如果URL有效,则会显示一个关于成功验证的消息。

CurrencyCode 参数用于指定用于付款的货币的代码。 UseSandbox 参数用于在PayPal沙箱和实际PayPal帐户之间进行 switch。 SendToReturnURL 参数用于打开/关闭发送到 return_url的通知。 建议仅将 SendToReturnURL 设置为 true,以便测试的目的。

源代码的使用

本文附有一个简化的在线商店源代码存档。 你可以在软件产品中使用这里代码,以便在你需要执行付款时支持与贝宝的交互。

要使用本文提供的代码,你需要在你的系统上安装. NET 框架 2.0或者更高版本。 你还需要有一个真实的或者沙箱贝宝帐户。 不需要额外的要求来打开沙箱帐户。 然而,要创建真正的PayPal账户,你需要在任何美国银行拥有一个信用卡和/或者一个。 确保标准 WebClient 服务在你的系统上运行。 如果有 Visual Studio 2005,则可以通过打开 paypal.sln 解决方案并执行代码来运行测试。 IPN在这里情况下不可用。 另一种选择是在IIS服务器上创建一个虚拟目录。

结束语

总之,我想给你们一些建议:

  • 在接收到 VERIFIED 响应之前,请不要信任网络连接脚本获得的数据。 应保留有关已经处理事务的信息。 因此,当收到 VERIFIED 响应时,你可以确保事务之前未处理。
  • 不要使用 payer_email 来标识购买者,因为 E-mail 可以被更改。 使用 payer_id
  • txn_type=web_accept 并不意味着卖家会收到付款。 你应该始终检查 payment_status=completed。 单个例外是它的帐户不是美国和 pending_reason=intl的卖家。
  • 在任何系统中都不可能出现故障,PayPal也不例外。 如果网络连接脚本接收到可以疑数据,应该写入日志,并且管理员应该通知。 为用户实现一个表单,以便能够通知你错误也是很有用的。

引用

历史记录

  • 14th 2007年06月 --原始版本发布
  • 27th添加了文章描述的示例的C# 实现( 请参见这里的 )。
  • 24th 2007年08月 --源代码下载更新
  • 第三方 2008年06月 --文章更新

USE  SYS  系统  asp  asp-net  支付  
相关文章