在go语言中与PayPal的IPN(即时付款通知)接口的示例
PayPal已经成为许多创业公司接受在线付款的标准(Stripe最近越来越流行,但他们还没有在全球范围内使用),本教程是关于如何配置你的Golang程序与PayPal的IPN(即时付款通知)接口。
下面是一个如何在Golang中与PayPal的IPN接口的例子:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strings"
)
// PayPal变量 -- 根据你的要求来改变它。
// 注意:我决定在这里使用单独的变量,这样我就可以包括
// 解释。如果把这些变量放到数组中(如url.Values{} ),并在其中加入解释,会更好、更 "干净"。
// 成数组(如url.Values{} https://golang.org/pkg/net/url/#Values ),然后
// 循环数组中的名称和值,在下面的表单隐藏字段中。
var currency_code = "USD"
//支付宝账户接收资金
var business = "[your email address at PayPal to receive money]"
//在PayPal顶部的图像
var image_url = "https://d1ohg4ss876yi2.cloudfront.net/logo35x35.png"
// 将socketloop.com:8080改为你的域名。
// 记住:localhost不工作,IPN模拟器只处理80或443端口。
var cancel_return = "http://[your domain]/paymentcancelreturn"
var return_url = "http://[your domain]/paymentsuccess" //return是Golang的关键字
var notify_url = "http://[your domain]/ipn" // <--- 对IPN的工作很重要!
//只是自定义字段的一个例子,可以是用户名,等等。使用自定义字段
// 用于额外的验证目的,或在数据库中标记PAID状态。
//在数据库中标记PAID状态,等等。
var custom = "donation"
// 见
// https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/
//了解 rm 和 _xclick 的含义。
var rm = "2" // rm 2 equal Return method = POST
var cmd = "_xclick"
var item_name = "Donation for SocketLoop"
var quantity = "1"
var amount = "5"
//取消注释以切换到真正的PayPal而不是沙盒。
//var paypal_url = "https://www.paypal.com/cgi-bin/webscr"
var paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr"
func Home(w http.ResponseWriter, r *http.Request) {
html := "<html><body><h1>You will be directed to PayPal now to pay USD " + amount + " to SocketLoop!</h1>"
html = html + "<form action=' " + paypal_url + "' method='post'>"
// now add the PayPal variables to be posted
// a cleaner way to create an array and use for loop
html = html + "<input type='hidden' name='currency_code' value='" + currency_code + "'>"
html = html + "<input type='hidden' name='business' value='" + business + "'>"
html = html + "<input type='hidden' name='image_url' value='" + image_url + "'>"
html = html + "<input type='hidden' name='cancel_return' value='" + cancel_return + "'>"
html = html + "<input type='hidden' name='notify_url' value='" + notify_url + "'>"
html = html + "<input type='hidden' name='return' value='" + return_url + "'>" //use return instead of return_url
html = html + "<input type='hidden' name='custom' value='" + custom + "'>"
html = html + "<input type='hidden' name='rm' value='" + rm + "'>"
html = html + "<input type='hidden' name='cmd' value='" + cmd + "'>"
html = html + "<input type='hidden' name='item_name' value='" + item_name + "'>"
html = html + "<input type='hidden' name='quantity' value='" + quantity + "'>"
html = html + "<input type='hidden' name='amount' value='" + amount + "'>"
html = html + " <input type='submit' value='Proceed to PayPal'></form></body></html>"
w.Write([]byte(fmt.Sprintf(html)))
}
func PaymentSuccess(w http.ResponseWriter, r *http.Request) {
// 在这里你可能想感谢用户的订单
// 或者其他什么。 这时的订单信息是在POST
// 变量中。 然而,你并不想 "处理 "这个订单,直到你
//从IPN中得到验证。 这时,你会有代码来
// 给管理员发电子邮件,更新数据库中的付款状态,激活一个
//会员资格,等等。
html := "<html><body><h1>Thank you! Payment accepted!</h1></body></html>"
w.Write([]byte(fmt.Sprintf(html)))
}
func PaymentCancelReturn(w http.ResponseWriter, r *http.Request) {
html := "<html><body><h1>Oh ok. Payment cancelled!</h1></body></html>"
w.Write([]byte(fmt.Sprintf(html)))
}
func IPN(w http.ResponseWriter, r *http.Request) {
// 付款已收到,IPN已验证。 这是你
// 更新你的数据库以激活或处理该订单,或设置
// 用户的订单细节,给管理员发电子邮件、
//等等。你可以通过IPN数据访问一系列的信息。
//你可以通过r.Form的IPN数据访问一系列的信息://查看paypal的文档,了解哪些信息
// IPN POST变量中的信息。 基本上,所有的POST变量
// Paypal发送的,我们送回去验证的。
// 在本教程中,我们只是打印出所有的IPN数据。
fmt.Println("IPN received from PayPal")
err := r.ParseForm() // need this to get PayPal's HTTP POST of IPN data
if err != nil {
fmt.Println(err)
return
}
if r.Method == "POST" {
var postStr string = paypal_url + "&cmd=_notify-validate&"
for k, v := range r.Form {
fmt.Println("key :", k)
fmt.Println("value :", strings.Join(v, ""))
// 注意:将IPN数据k,v存储到一个片断中。它对以后的数据库输入很有用。
postStr = postStr + k + "=" + url.QueryEscape(strings.Join(v, "")) + "&"
}
// 为了验证来自PayPal的信息,我们必须按照收到的顺序发送
// 准确地按照收到的顺序发回内容,并在其前面加上
//命令_notify-validate
// 然后,PayPal将发送一个单字的信息,要么是VERIFIED、
// 如果信息是有效的,就发送VERIFIED,如果信息是无效的,就发送INVALID。
// 更多信息请见
// https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNIntro/
//将数据传回PayPal
client := &http.Client{}
req, err := http.NewRequest("POST", postStr, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type: ", "application/x-www-form-urlencoded")
// fmt.Println(req)
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Response : ")
fmt.Println(resp)
fmt.Println("Status :")
fmt.Println(resp.Status)
// 将响应转换为字符串
respStr, _ := ioutil.ReadAll(resp.Body)
//fmt.Println("Response String : ", string(respStr))
verified, err := regexp.MatchString("VERIFIED", string(respStr))
if err != nil {
fmt.Println(err)
return
}
if verified {
fmt.Println("IPN verified")
fmt.Println("TODO : Email receipt, increase credit, etc")
} else {
fmt.Println("IPN validation failed!")
fmt.Println("Do not send the stuff out yet!")
}
}
}
func main() {
// http.Handler
mux := http.NewServeMux()
mux.HandleFunc("/", Home)
mux.HandleFunc("/paymentcancelreturn", PaymentCancelReturn) // remember, case sensitive
mux.HandleFunc("/paymentsuccess", PaymentSuccess) // remember, case sensitive
mux.HandleFunc("/ipn", IPN)
http.ListenAndServe(":8080", mux)
}
把域名改成你的,然后运行代码。
将你的浏览器指向主要的URL会显示这个页面:
并在点击按钮后:
付款后跟进:
最后,在IPN信息被贴回PayPal并被核实后:
r.Form包含IPN的有用数据。
使用这些数据进行额外的验证,例如在发布到PayPal之前确保金额是相同的。
电子邮件或用户名(通过自定义字段)是相同的,然后再发布到PayPal。
这些额外的安全步骤是为了确保在发布过程中没有数据改变(欺骗)。
参考资料 :
https://github.com/paypal/ipn-code-samples/blob/master/paypal_ipn.pl
https://github.com/asadovsky/tadue/blob/master/app/paypal.go
https://www.socketloop.com/tutorials/golang-parsing-or-breaking-down-url
相关文章