Paypal IPN接口开发小结

最近终于搞定了该死的Paypal ipn,特地在此留下点东西备忘。
所谓paypal ipn(Instant Payment Notification),就是Paypal开发的一种能主动通知第三方卖家系统交易状态的一种机制。IPN的原理很简单,就是当产生了一个交易之后,交易状态发生变化时,如用户已经付款、或者退款、撤销时,Paypal利用常用的HTTP POST方式,将交易的一些变量提交给网站的某个页面(称之为IPN Handler),当这个页面接受到请求时候,将这些数据原封不动加上一个指示验证的cmd=_notify-validate,POST回Paypal的接口地址,如果数据正确,那么Paypal返回字符串VERIFIED,否则为INVALID,如果结果为VERIFIED,那么你的程序就可以使用这些数据进行操作。
但代码的调试是一件很痛苦的事情,因为作为第三方开发人员,不可能开两个帐号,每次测试还要之间交易一些钱,所以Paypal专门开发了Sandbox给开发人员进行开发,首先到https://developer.paypal.com/ 注册一个开发帐号,好了之后再进入Sandbox建立测试用的Paypal虚拟帐号(至少应该建立一个Business的和一个Personal的),这种账号注册方法和Paypal的流程一样,信息可以是假的,包括银行帐号、信用卡(其实Paypal Sandbox会自动生成一些随机的号码)。接下来需要激活Paypal Sandbox的虚拟帐号,注意,这里不管你在Paypal Sanbox注册时填什么邮件地址,有任何发送到虚拟帐号所填邮箱的邮件都存会在开发帐号的管理界面中的Email页(导航栏上有)中。登录Sandbox的虚拟Paypal环境,还需要验证虚拟帐号的银行,这里可以随便填,然后通过Add Funds来给账户充值(想填多少填多少)。然后,还需要激活IPN的选项,在Business的那个账户的Profile设置页面中,点击,然后点击Edit按钮,打开IPN,这里如果你使用的是固定的IPN Handle,可以直接将地址填入。
接下来,我们测试的时候,应该将Paypal接口的地址设置为https://www.sandbox.paypal.com/cgi-bin/webscr
最后基本的流程为:

  • 用户在我们的网站上选择商品、放入购物车,然后检查准备支付
  • 网站根据购物车中的商品,生成Paypal的支付表单(也是提交到上面IPN用的Paypal接口地址),包含了此次交易的一些信息(下次会专门找机会解释一下)。并在自己的数据库中生成一张订单记录。
  • Paypal在Session中记录下这些交易信息
  • 用户用Paypal账户登录Paypal(Sandbox用Sandbox的虚拟帐号),复查明细,点击Pay按钮
  • Paypal进行交易处理,如果我们的Paypal收款帐号在接受帐款上没有什么问题(没有特别的需要在界面上“Accept”的地方),交易完成,那么Paypal会发送一个IPN,并发送提示邮件。
  • 我们IPN Handler接受到信息,首先向Paypal进行校验,如果信息正确,然后根据信息和自己数据库中进行比对,如果无误,可以将支付信息保存,并修改订单状态。
  • 然后Paypal会显示一个界面表示交易完成,此时如果用户点击“Return”按钮,Paypal会将用户送回我们网站指定地点。
  • 我们网站迎接用户回来,向用户表示感谢,并进行提醒,给出订单号等等。

整个流程基本如下,不过其中有很多要注意的地方:

  1. 我们看到了,接口地址是https开头的,也就是要求使用SSL进行连接,其实Paypal Sandbox可以使用http,但是最后实际的Paypal接口,不支持http协议,所以如果你用PHP写IPN Handler脚本的话,在验证信息的代码部分,如果简简单单使用fopen、HTTP_Request之类的工具是没用的,PHP的socks库不支持SSL,应该使用curl,有两种方法,一个是使用PHP扩展中的libcurl,Paypal官方发布的SDK中便是使用的这个方法,但Paypal的SDK非常复杂,如果想自己写,可以使用Snoopy,一个简单实用的PHP请求库(它自己说是模拟了浏览器的行为),而它使用的是Curl的可执行文件,通过管道获得结果,而非php的libcurl,所以自己cURL的主页上下载一个支持SSL的编译版本,可能还需要安装OpenSSL,不过对于Linux系统,可能应该会都带。
  2. 在支付表单中,可以自己设置notify_url字段,来指定此次交易的信息应该发送到哪个地方,这样就可以覆盖在Profile中我们的设置,另外,这个字段要进行urlencode
  3. 我们得到的IPN信息中,status对应的便是交易状态,如Complete表示完成,首字母大写,而验证结果则是VERIFIEY或者INVALID,全部大写,具体的内容,可以查看Paypal官方的文档订单管理整合指南

缓存友好的网页

缓存友好的网页

by Jennifer Vesperman 03/07/2002 原文地址:http://www.linuxdevcenter.com/pub/a/linux/2002/02/28/cachefriendly.html

翻译:ShiningRay @ Nirvana Studio

现在我们有很多HTTP缓存。他们存放你的页面多久呢?他们应该存放多久呢?RFC 2616(HTTP/1.1)指出了缓存必须遵循ExpiresCache-Control头——那么你的页面是否都有呢?

“在HTTP/1.1种的缓存的目的是为了在很多情况下消除发送请求的需要,同时在其他一些情况下消除发送完整的响应的需要。” RFC 2616

缓存友好页面的优点

“当缓存可以完全避免对原始服务器发送请求时,HTTP缓存的工作是最佳的。避免请求的主要机制是,针对原始服务器提供未来的一个确切的失效时间,表示一个响应可以被用来满足后续的请求。换句话说,一个缓存可以返回一个新的响应而不必先联系服务器。”RFC 2616

RFC写的时候是期望网页要包含失效期头的。如果谨慎地选择头中的失效时间,缓存可以不失去任何意义来提供存储了的页面。

当原始服务器不提供失效头时,缓存根据诸如“Last-Modified”之类的头来推断,猜测出一个合适的失效时间。与由了解页面内容以及更改频率的人设置的失效日期相比,推断的方法显然效率较低。

“由于推断的失效时间也许会降低语义透明度,应该被谨慎使用,同时我们鼓励原始服务器尽可能提供确切的失效时间。”——RFC 2616

关于缓存的注意事项

HTTP/1.1标准(第13节)关于缓存有一个强制的要求:它要求他们提供语义的透明——返回一个缓存了的响应必须提供本来从原始服务器上获取的同样的数据;同时它提倡读取原始服务器和客户端所提供的新鲜度要求。

缓存必须传递由上游缓存或者是原始服务器提供的警告,而且如果提供了一个陈旧的响应,他们也必须加入警告。一个缓存可以在特定的情况下提供一个陈旧的响应,大部分情况下是如果缓存无法连接到原始服务器同时客户端声明它可以接受一个陈旧的响应。

如果一个缓存收到了针对一个陈旧页面的请求,它发送一个验证请求询问原始服务器页面是否已经更改。最常见的验证工具便是最后的更新时间。如果在一秒钟内存储了两次更改,Last-Modified将会不正确。因此,HTTP/1.1利用Entity Tag头提供了更加严格的验证。

最简便的协助缓存的方法是保持你的HTTP服务器上的时间精确且总是发送DateLast-Modified头。

另外,要成为一个真正的缓存友好的站长,还要在你的页面中加入缓存头。

可用的缓存头

Expires头是最快捷最方便的解决方案。这个头声明了页面被认为不再可被缓存的时间,在这之后,任何保存了这个页面副本的缓存都应该联系原始服务器。语法是:

例如:

若要标记一个响应为已经“已经过期”或者“不可缓存”,头则应该设置为发送响应的时间。若要标记一个响应为“永不过期”,则应该将头设置为未来的一年。

另一个头是Cache-ControlCache-Control包括了这些元素:指明页面元素最大时限,他应该如何被缓存的,他如何被转换到另一个不同的媒介,以及他如何被存放在持久媒介中的。

本文使用Apache为例,设置头并在例子中详细讨论Cache-Control头。

在Apache中设置缓存头

#主要的方法:Expires

要使用Expires头,你需要运行在Apache 1.2或者更新的版本上,同时要启用 mod_expires 模块。去掉httpd.conf文件在“Dynamic Object Support”一节中的 expires_module 一行上的注释,然后重新编译Apache。

(如果你运行的是Apache 1.3或更新的版本,同时它已经配置为运行时加载模块,你可以编辑httpd.conf然后重新启动Apache而无需重新编译。)

mod_expires 基于三条指令来计算Expires头。这些指令可以应用于文档范围同时也可以在任何以下范围内使用:“server config”、“virutal host”、“directory”、或者“.htaccess”。

Expires指令有两种语法。其中一个有点难以阅读;它要你计算到失效为止用的秒数。幸运的是,这个模块同样可以读取一个更加人性化的语法。本文将解释较为可读的语法。

要用到的指令是:

base 可以是以下其中之一:

  • access
  • now (等同于“access”)
  • modification

num 是一个整数值,单位是 type :

  • years
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds

如果你准备对一个服务器、虚拟主机或者是目录使用Expires指令,编辑 httpd.conf 文件并在所需的范围内加入以下指令。

如果你要在 .htaccess 文件中使用Expires头,那么你要先编辑httpd.conf 设置相应的目录的AllowOverride。Apache只会读取设置了“Indexes”覆盖的目录中的 .htaccess

在相应目录里的.htaccess文件中加入Expires指令。站长可以编辑.htaccess文件而无需修改httpd.conf

.htaccess”方法的主要问题是Indexes覆盖,这样.htaccess文件将给予站长更多的配置选项而非仅仅Expires头。这也许并不是系统管理员所期望的。

#候选方法:Cache-Control

mod_cern_meta允许文件级的控制,同时它也可以使用Cache-Control头(或任何其他头)。响应头是放在原始目录的子目录中,根据原始文件名所命名的一个文件。

去掉cern_meta_module 一行的注释并重新编译,和上一节中对expires_module 的一样。

httpd.conf 文件中,打开MetaFiles on,将 MetaDirectory 设置为子目录名,同时把MetaSuffix设为头的文件的后缀名。

使用这些值的话,文件 /var/www/www.example.org/index.html 将会以 /var/www/www.example.org/.web/index.html.meta. 为元文件。

任何有效的HTTP头都可以放在这些文件中。这提供了另一种使用Expires头的方式,同时它可以加入Cache-Control头。相应的Cache-Control头如下:

修改失效机制,将覆盖Expires头。Max-age隐含了Canche-Control: public

表示对象可以被存在缓存中。这是默认值。

表示对象(或指定字段)不能被保存在一个共享的缓存中同时是针对一个单独的用户的。它可以被保存在一个私有的缓存中。

表示对象(或者指定字段)可以被缓存,但不能直接给客户除非经过原始服务器的重新验证。

表示条目不能存储在持久的存储媒介中,同时应该尽可能快地从非未定存储媒介中删除。

代理可以将数据从一个存储系统中转换到另外一个。这个指令表示(大多数)响应不能被转换。(RFC允许某些字段的转换,即使存在这个头)

强制代理重新验证该页面即使客户可以接受一个陈旧的响应。请在使用这些头之前阅读RFC,关于他们的使用有一些限制。

警告

  • HTTP/1.0有一个很小的缓存控制机制,仅能理解Pragma: no-cache头。使用HTTP/1.0的缓存将忽略ExpiresCache-Control头。
  • 任何一个Cache-Control指令都不能保证隐私性或者数据的安全性。“private”和“no-store”指令可以为隐私性和安全性方面提供一些帮助,但是他们并不能用于替代身份验证和加密。
  • 本文不能代替RFC。如果你要实现Cache-Control头,请阅读RFC来获取每个头的含义和限制的详细描述。

#最后的话

缓存是Internet的现实问题同时它能让带宽的使用更加有效。你的客户也许是通过一个缓存来浏览你的页面的,有时候还会用多个缓存。给你的页面加上缓存头可以保护你的页面内容也可以让你的客户节省他们的带宽。

#进一步阅读

Jennifer Vesperman 是《Essential CVS》一书的作者。她为O’Reilly Network、Linux Documentation Project撰稿,有时也为Linux.com。

HTTP代理缓存

原文地址:http://vancouver-webpages.com/proxy.html

翻译:ShiningRay @ Nirvana Studio

什么是代理缓存?
下面是使用代理服务器的三大理由:

  1. 因为你在防火墙背后(为了安全),因此必须使用。
  2. 因为使用缓存可以显著提高任何人的页面浏览速度。
  3. 因为对于你的机器,你没有足够的“真实”IP可用。

如果你在防火墙后面,那么你其实可能已经使用了一个代理。否则,你要考虑安装一个。

Netscape Navigator 和一些其他较新的浏览器内建了缓存机制。在一个单用户系统中,诸如使用拨号的PC机,这应该足够了。你可以调整缓存参数让缓存变得大一点,或更频繁地检查缓存条目。顺带提一下,在Netscape中,“刷新”通常不会去获取一份文档完全最新的副本,他会发送一个GET If-Modified-Since以及Pragma: no-cache。按下Shift再按“刷新”会强制所有页面框架都通过发送Pragma: no-cache来完全从来源重新加载。如果要在Netscape中查询磁盘缓存输入about:cache

即使在单个系统上,如果你使用多于一个的浏览器或者有多于一个的用户,一个代理缓存也许会更有效,因为缓存的文档可以在各种媒介中共享。

LAN系统

多个用户在LAN中使用代理就可以产生的真正的好处。由任何一个人访问的任何一个新的页面都会被存储在缓存中。下一个同样访问这个页面的人将直接获取缓存了的副本,以完全的LAN的网速,而不用再去访问源地址。这可能会快1000倍甚至更多。

针对Windows系统:

配置浏览器的代理

一些浏览器可能只接受一个值,例如(在Unix中)可以通过使用 setenv http_proxy http://somewhere.org:80/。某些域名可以被排除,通过使用 setenv no_proxy some.org,some.other.org。其他浏览器,如Netscape,有一个更复杂的方式来支持多种代理。Netscape还有一个通过使用JavaScript来自动处理代理的方式。Mosaic-2.7页运行代理列表。

绕过缓存

如果一个HTTP请求含有Pragma: no-cache的头,那么缓存就会被指向获取一个新的副本。可能它自己也会保存一个新的副本。在Netscape、Mosaic和Lynx(可能所有的浏览器)中使用刷新都会发送一个带有这个头的请求。

可缓存和不可缓存的文档

一般的HTML文档通常都是可以缓存的。缓存代理一般要求一个有效的Last-Modified头,同时可能不缓存大于某个尺寸或主题受限制的对象。由CGI脚本生成的HTML文档通过生成合法的Expires头来变得可缓存(或不可),虽然某些代理不会缓存带有“cgi-bin”或查询字符串的URL。要求身份验证的文档一般都不会被缓存。Netscape有一个选项让你可以从本地缓存SSL服务器上获取的文档。如果你打开了这个选项,其他获得了你的机器访问权限的人(很有可能是窃取的)就可以读取你近期的安全事务。

注意不同的缓存服务器也许在解释HTTP标准上会略有不同,所以某一个可以缓存的文档不一定能被另一个所缓存。

缓存控制和CGI

我使用缓存测试脚本针对Apache 1.1.1和Squid 1.0.5进行测试,获得了以下结果:I

Expires

Last-Modified

Apache

Squid

Tonight

Last Night

缓存

缓存

+1 minute

Last Night

Expires

Expires

Tonight

none

Not Cached

Cached

none

none

Not Cached

Cached

0

Last Night

Not Cached

Not Cached

Last Night

Last Night

Not Cached

Not Cached

Tonight

Tonight

Cached

Cached

Tonight

0

Not Cached

Cached

Squid缓存的默认配置是不去缓存中间有“cgi-bin”和“?”的URL,我去掉了这个配置,获得了这种结果。

RFC1945 (HTTP1.0标准)说Expires过期时间等于或早于Date头的值,接受者便不能缓存文档。零值(0)或一个非法的日期格式被认为等同于“立刻失效”。

建议在 CGI 脚本中使用:如果CGI脚本的输出确实是一个静态的文档,对于同一个查询字符串(如/cgi-bin/search?query=food)有同样的内容,那么可以生成一个合适的Last-Modified字段(最后修改日期),以RFC1123的格式。如果输出的内容在某个特定的时间段内是合法的,那么生成合适的Expires头。如果输出的内容里立刻就要失效,或者失笑于某个特定时间,则生成一个Expires头等同于当前时间或一个非法值。

为了能更好的使用全部的带宽,尽可能地多缓存东西。也就是说,例如,如果你有一个网络摄像头,显示办公室窗口内的景象,你也许可以生成一个Expires头10分钟或更多。

服务器端包含(SSI)

Apache, NCSA, 以及一些其他的服务器可以在HTML(.shtml)中使用服务器端包含(Server-Side Include)。由于文档的内容是由几个被引用的文件组成的,服务器一般不会设置一个Last-Modified日期或者Content-Length。相应的,这类文档是无法缓存的(因此也要比其他使用缓存的人载入慢很多)。Apache支持一个选项叫做XBitHack,它可以发送一个Last-Modified日期。如果你用了这个,你必须在任何一个被引用的文件更改的时候,把.shtml包装器文件更改为最新时间(Unix命令为touch),否则其他使用缓存的人将无法看到你的新文档除非他们明确使用更新。

Content-Negotiation(内容判断)

如果正在使用内容判断,来提供不同的语言或图像类型,那么对于不同的内容可能只有一个URL。相应地,这样Apache服务器就不会设置Last-Modified。这个问题在HTTP1.1草案中已经通过Vary头来解决。

文档过期

代理缓存会看Expires(过期)头同时使用它在缓存中设置一个失效的日期。如果不存在这个HTTP头,那么将假设为默认的生命周期。这时我不知道代理检查了HTML内容中的META标签;然而CERN以及新的Apache服务器可能会使用一个元数据文件方案来在文档头中生成额外的字段如Expires。CGI脚本也可以使用类似LWP Perl的库来生成合适的字段。一个非法的Expires头,例如0、负数等,也会使文档无法缓存(里可以失效)。

谁在使用代理

尽管RFC1945推荐不要更改User-Agent字段,特定的代理仍会这样,于是我们可以通过它来统计信息。大约8%的浏览本页面的用户使用以下的代理之一:

  • CERN-HTTPD

  • Harvest Cache

  • Squid Cache

所有在防火墙之后的用户必须使用代理,虽然缓存是完全另一个问题。SOCKS则是一个无缓存的代理方式。

NLANR 缓存项目

看看NLANR的分布式缓存项目!

未来

HTTP1.1(ds.internic.net正在制作草案)有更多针对代理服务器的方案。我们也许可以拥有很多交互的代理缓存可以极高地提高整个互联网。任何有快于14.4kbps moderm的人都会受益。

另一个发展是预抓取代理缓存,例如 Wcol: WWW Collector。这里,这个代理积极的提前寻找相关的图像和页面。原来仅针对Mosaic提速,现在他可以和一个层次缓存机制相互协作当用户读取了第一个页面之后来获取相关的页面。