-
Notifications
You must be signed in to change notification settings - Fork 23
/
paypal.go
128 lines (112 loc) · 4.71 KB
/
paypal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package paypal
import "github.com/LunaNode/lobster"
import "bytes"
import "errors"
import "fmt"
import "io/ioutil"
import "net/http"
import "net/url"
import "strconv"
import "strings"
const PAYPAL_URL = "https://www.paypal.com/cgi-bin/webscr"
const PAYPAL_CALLBACK = "/paypal_notify"
type PaypalTemplateParams struct {
Frame lobster.FrameParams
Business string
Amount float64
UserId int
NotifyUrl string
ReturnUrl string
Currency string
RequireShipping bool
}
type PaypalPayment struct {
business string
returnUrl string
requireShipping bool
}
func MakePaypalPayment(business string, returnUrl string, requireShipping bool) *PaypalPayment {
this := new(PaypalPayment)
this.business = business
this.returnUrl = returnUrl
this.requireShipping = requireShipping
lobster.RegisterHttpHandler(PAYPAL_CALLBACK, this.Callback, true)
return this
}
func (this *PaypalPayment) Payment(w http.ResponseWriter, r *http.Request, frameParams lobster.FrameParams, userId int, username string, amount float64) {
cfg := lobster.GetConfig()
frameParams.Scripts = append(frameParams.Scripts, "paypal")
params := &PaypalTemplateParams{
Frame: frameParams,
Business: this.business,
Amount: amount,
UserId: userId,
NotifyUrl: cfg.Default.UrlBase + PAYPAL_CALLBACK,
ReturnUrl: this.returnUrl,
Currency: cfg.Billing.Currency,
RequireShipping: this.requireShipping,
}
lobster.RenderTemplate(w, "panel", "paypal", params)
}
func (this *PaypalPayment) Callback(w http.ResponseWriter, r *http.Request) {
cfg := lobster.GetConfig()
requestBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
lobster.ReportError(err, "paypal callback read error", fmt.Sprintf("ip: %s", r.RemoteAddr))
w.WriteHeader(403)
return
}
// decode the post data manually since there may be encoding issues
requestParts := strings.Split(string(requestBytes), "&")
myPost := make(map[string]string)
for _, part := range requestParts {
keyval := strings.Split(part, "=")
if len(keyval) == 2 {
myPost[keyval[0]], _ = url.QueryUnescape(keyval[1])
}
}
// post back to Paypal system to validate the IPN data
validateReq := "cmd=_notify-validate"
for key, value := range myPost {
validateReq += fmt.Sprintf("&%s=%s", key, url.QueryEscape(value))
}
resp, err := http.Post(PAYPAL_URL, "application/x-www-form-urlencoded", bytes.NewBufferString(validateReq))
if err != nil {
lobster.ReportError(err, "paypal callback validation error", fmt.Sprintf("ip: %s; requestmap: %v", r.RemoteAddr, myPost))
w.WriteHeader(403)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
lobster.ReportError(err, "paypal callback validation error", fmt.Sprintf("ip: %s; requestmap: %v", r.RemoteAddr, myPost))
w.WriteHeader(403)
return
}
if string(body) != "VERIFIED" || myPost["payment_status"] == "" || myPost["mc_gross"] == "" || myPost["mc_currency"] == "" || myPost["txn_id"] == "" || myPost["receiver_email"] == "" || myPost["payment_status"] == "" || myPost["payer_email"] == "" || myPost["custom"] == "" {
lobster.ReportError(errors.New("missing field or not verified"), "paypal callback bad input or validation", fmt.Sprintf("ip: %s; verify body: %s; requestmap: %v", r.RemoteAddr, body, myPost))
w.WriteHeader(403)
return
}
w.WriteHeader(200)
if myPost["payment_status"] != "Completed" {
return
} else if !strings.HasPrefix(myPost["custom"], "lobster") {
lobster.ReportError(fmt.Errorf("invalid payment with custom=%s", myPost["custom"]), "paypal callback error", fmt.Sprintf("ip: %s; requestmap: %v", r.RemoteAddr, myPost))
return
} else if strings.TrimSpace(strings.ToLower(myPost["receiver_email"])) != strings.TrimSpace(strings.ToLower(this.business)) {
lobster.ReportError(fmt.Errorf("invalid payment with receiver_email=%s", myPost["receiver_email"]), "paypal callback error", fmt.Sprintf("ip: %s; requestmap: %v", r.RemoteAddr, myPost))
return
} else if myPost["mc_currency"] != cfg.Billing.Currency {
lobster.ReportError(fmt.Errorf("invalid payment with currency=%s", myPost["mc_currency"]), "paypal callback error", fmt.Sprintf("ip: %s; requestmap: %v", r.RemoteAddr, myPost))
return
}
paymentAmount, _ := strconv.ParseFloat(myPost["mc_gross"], 64)
transactionId := myPost["txn_id"]
userIdStr := strings.Split(myPost["custom"], "lobster")[1]
userId, err := strconv.Atoi(userIdStr)
if err != nil {
lobster.ReportError(fmt.Errorf("invalid payment with custom=%s", myPost["custom"]), "paypal callback error", fmt.Sprintf("ip: %s; requestmap: %v", r.RemoteAddr, myPost))
return
}
lobster.TransactionAdd(userId, "paypal", transactionId, "Transaction "+transactionId, int64(paymentAmount*lobster.BILLING_PRECISION), 0)
}