| @@ -2,7 +2,6 @@ package main | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "log" | |||
| ) | |||
| @@ -59,6 +58,6 @@ func readConfigFile(file string) error { | |||
| log.Fatal("cannot print back to json") | |||
| return err | |||
| } | |||
| fmt.Println(string(j)) | |||
| log.Println(string(j)) | |||
| return err | |||
| } | |||
| @@ -21,7 +21,7 @@ func (m *TransactionDB) conn(c AppConfig) error { | |||
| dbUser := c.DB.User | |||
| dbPass := c.DB.Pass | |||
| dbName := c.DB.Schema | |||
| h, err := sql.Open(dbDriver, dbUser+":"+dbPass+"@/"+dbName) | |||
| h, err := sql.Open(dbDriver, dbUser+":"+dbPass+"@/"+dbName+"?parseTime=true") | |||
| if err != nil { | |||
| m.h = nil | |||
| panic(err.Error()) | |||
| @@ -2,8 +2,7 @@ package main | |||
| import ( | |||
| "errors" | |||
| "github.com/go-sql-driver/mysql" | |||
| "time" | |||
| ) | |||
| type LeanworkRequest struct { | |||
| @@ -17,7 +16,7 @@ type LeanworkRequest struct { | |||
| CustomerId string | |||
| Sign string | |||
| Valid bool | |||
| Ts mysql.NullTime | |||
| Ts time.Time | |||
| Ip4 uint32 | |||
| Ip4Location string | |||
| } | |||
| @@ -1,169 +1,30 @@ | |||
| package main | |||
| import ( | |||
| "database/sql" | |||
| "errors" | |||
| "fmt" | |||
| "log" | |||
| "math" | |||
| "net/http" | |||
| "net/url" | |||
| "strconv" | |||
| "time" | |||
| "github.com/go-sql-driver/mysql" | |||
| ) | |||
| type RpnOut struct { | |||
| Version string | |||
| Sign_type string | |||
| Mid string | |||
| Notify_url string | |||
| Order_amount string | |||
| Order_time string //YYYYMMDDHHMMSS | |||
| Order_id string | |||
| User_id string | |||
| User_name string | |||
| User_cardno string | |||
| Signature string | |||
| //template | |||
| Url string //where to post entire data structure | |||
| //database specific | |||
| Id int64 | |||
| Leanwork int64 | |||
| Ip4 uint32 | |||
| Ip4location sql.NullString | |||
| Ts mysql.NullTime | |||
| } | |||
| // //build request from leanwork request forms | |||
| // func (m *RpnOut) buildReqByForm(form url.Values) RpnOut { | |||
| // r := RpnOut{} | |||
| // r.version = "1.1" | |||
| // r.sign_type = "MD5" | |||
| // r.mid = "EU85201311P2P" | |||
| // r.notify_url = "http://rpn.supertraderfx.ayvwd8em49pvoa3g.com/rpn_notify" | |||
| // r.order_id = form["orderNo"][0] | |||
| // r.order_amount = form["orderAmount"][0] //cents | |||
| // r.order_time = m.now() | |||
| // r.user_id = form["customerId"][0] | |||
| // r.user_name = "SuperForex" | |||
| // r.user_cardno = "6212262002002377849" | |||
| // r.signature = md5RpnFormP2P(r) | |||
| // *m = r | |||
| // return r | |||
| // } | |||
| func (m *RpnOut) buildReqByLeanworkRequestP2P(row LeanworkRequest, user_name string, user_cardno string) RpnOut { | |||
| m.Version = "1.1" | |||
| m.Sign_type = "MD5" | |||
| m.Mid = Config.Rpn.MIDP2P | |||
| m.Notify_url = Config.Rpn.UrlCallBack | |||
| m.Order_id = row.OrderNo | |||
| m.Order_amount = m.translateAmountFromLeanwork(row.OrderAmount) | |||
| m.Order_time = m.now() | |||
| m.User_id = row.CustomerId | |||
| m.User_name = user_name | |||
| m.User_cardno = user_cardno | |||
| m.Signature = md5RpnFormP2P(*m) | |||
| m.Leanwork = row.Id | |||
| return *m | |||
| } | |||
| func (m *RpnOut) buildReqByLeanworkRequestFAT(row LeanworkRequest, user_name string, user_cardno string) RpnOut { | |||
| m.Version = "1.1" | |||
| m.Sign_type = "MD5" | |||
| m.Mid = Config.Rpn.MIDFAT | |||
| m.Notify_url = Config.Rpn.UrlCallBack | |||
| m.Order_id = row.OrderNo | |||
| m.Order_amount = m.translateAmountFromLeanwork(row.OrderAmount) | |||
| m.Order_time = m.now() | |||
| m.User_id = row.CustomerId | |||
| m.User_name = user_name | |||
| m.User_cardno = user_cardno | |||
| m.Signature = md5RpnFormFAT(*m) | |||
| m.Leanwork = row.Id | |||
| return *m | |||
| } | |||
| //from 0.1 to 10 cents | |||
| func (m *RpnOut) translateAmountFromLeanwork(from string) string { | |||
| f, _ := strconv.ParseFloat(from, 32) | |||
| f = f * 100 //convert to cents | |||
| t := int(math.Ceil(f)) | |||
| s := strconv.Itoa(t) | |||
| return s | |||
| } | |||
| func (m *RpnOut) now() string { | |||
| t := time.Now() | |||
| return t.Format("20060102150405") | |||
| } | |||
| //send request to RPN to initiate transaction | |||
| func (m *RpnOut) SendReq(form url.Values) (*http.Response, error) { | |||
| return nil, nil | |||
| // r := m.buildReqByForm(form) | |||
| // hc := http.Client{Timeout: 15 * time.Second} | |||
| // myForm := url.Values{} | |||
| // myForm.Set("version", r.version) | |||
| // myForm.Set("sign_type", r.sign_type) | |||
| // myForm.Set("mid", r.mid) | |||
| // myForm.Set("notify_url", r.notify_url) | |||
| // myForm.Set("order_id", r.order_id) | |||
| // myForm.Set("order_amount", r.order_amount) | |||
| // myForm.Set("order_time", r.order_time) | |||
| // myForm.Set("user_id", r.user_id) | |||
| // myForm.Set("user_name", r.user_name) | |||
| // myForm.Set("user_cardno", r.user_cardno) | |||
| // myForm.Set("signature", r.signature) | |||
| // //req, err := http.NewRequest("POST", "https://lawipac.com/dumprequest.php", strings.NewReader(myForm.Encode())) | |||
| // req, err := http.NewRequest("POST", Config.Rpn.UrlTest, strings.NewReader(m.encode())) | |||
| // if err != nil { | |||
| // panic("wrong") | |||
| // } | |||
| // req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | |||
| // req.Header.Add("content-Length", strconv.Itoa(len(form.Encode()))) | |||
| // return hc.Do(req) | |||
| //Config.Rpn.UrlTest | |||
| // return http.PostForm(Config.Rpn.UrlTest, myForm) | |||
| //return http.PostForm("https://lawipac.com/dumprequest.php", myForm) | |||
| } | |||
| //encode without disturbing it's original order | |||
| func (m *RpnOut) encode() string { | |||
| s := "version=" + m.Version | |||
| s += "&sign_type=" + m.Sign_type | |||
| s += "&mid=" + m.Mid | |||
| s += "¬ify_url=" + url.QueryEscape(m.Notify_url) | |||
| s += "&order_id=" + m.Order_id | |||
| s += "&order_amount=" + m.Order_amount | |||
| s += "&order_time=" + m.Order_time | |||
| s += "&user_id=" + m.User_id | |||
| s += "&user_name=" + url.QueryEscape(m.User_name) | |||
| s += "&user_cardno=" + m.User_cardno | |||
| s += "&signature=" + m.Signature | |||
| return s | |||
| } | |||
| func retrieveFormValue(form url.Values, key string) (r string, err error) { | |||
| func rpnNotify(w http.ResponseWriter, r *http.Request) { | |||
| if r.Method != "POST" { | |||
| errPage(w, http.StatusMethodNotAllowed, "invalid request") | |||
| return | |||
| } | |||
| ri, err := GetRpnInFromHTTPRequest(r) //ParseForm called | |||
| if err != nil { | |||
| errPage(w, http.StatusBadRequest, "invalid parameters") | |||
| return | |||
| } | |||
| if _, ok := form[key]; ok { | |||
| r = form[key][0] | |||
| err = nil | |||
| } else { | |||
| r = "" | |||
| err = errors.New("Key [" + key + "] not found in HTTP request") | |||
| ro, _ := getRpnOutByOrderId(ri.Order_id) | |||
| ri.Leanwork = ro.Leanwork | |||
| ri1, err := ri.add2db() //TODO:check error add | |||
| if err != nil { | |||
| log.Printf("failed to add rpnIn %+v , error is %s", ri, err.Error()) | |||
| } | |||
| return r, err | |||
| } | |||
| func rpnNotify(w http.ResponseWriter, r *http.Request) { | |||
| fmt.Fprint(w, "ok notify") | |||
| fmt.Fprintf(w, "[SUCCESS]") | |||
| return | |||
| } | |||
| //receive RPN user name and card number | |||
| @@ -205,32 +66,3 @@ func rpnNameAndCard(w http.ResponseWriter, r *http.Request) { | |||
| //build rpn redirect page and send it | |||
| ro.sendRedirect(w, row) | |||
| } | |||
| func (m *RpnOut) sendRedirect(w http.ResponseWriter, row LeanworkRequest) { | |||
| //execute redirect | |||
| m.Url = Config.Rpn.Url | |||
| tmpl.ExecuteTemplate(w, "rpnCallOutRedirect", *m) | |||
| } | |||
| func getRpnOutByOrderId(order_id string) (ret RpnOut, err error) { | |||
| if err = db.conn(Config); err != nil { | |||
| return | |||
| } | |||
| defer db.close() | |||
| q := "SELECT * FROM rpnOut WHERE order_id = ? ORDER BY id DESC LIMIT 1" | |||
| err = db.h.QueryRow(q, order_id).Scan( | |||
| &ret.Id, &ret.Leanwork, &ret.Version, | |||
| &ret.Sign_type, &ret.Mid, &ret.Notify_url, | |||
| &ret.Order_amount, &ret.Order_time, &ret.Order_id, | |||
| &ret.User_id, &ret.User_name, &ret.User_cardno, &ret.Signature, | |||
| &ret.Ip4, &ret.Ip4location, &ret.Ts) | |||
| if err != nil { | |||
| if err == sql.ErrNoRows { | |||
| log.Println("trying to retrieve rpnOut(order_id=" + order_id + ") but not found") | |||
| } else { | |||
| log.Println("Error retrieving rpnOut(order_id=" + order_id + ") encountered : " + err.Error()) | |||
| } | |||
| } | |||
| return | |||
| } | |||
| @@ -2,10 +2,11 @@ package main | |||
| import ( | |||
| "database/sql" | |||
| "errors" | |||
| "log" | |||
| "net/http" | |||
| "strconv" | |||
| "github.com/go-sql-driver/mysql" | |||
| "time" | |||
| ) | |||
| type RpnIn struct { | |||
| @@ -19,7 +20,8 @@ type RpnIn struct { | |||
| Pay_amount string | |||
| Pay_result string | |||
| Signature string | |||
| Ts mysql.NullTime | |||
| Ts time.Time | |||
| Ip4 uint32 | |||
| } | |||
| //get given RpnIn record based on ID | |||
| @@ -34,7 +36,7 @@ func getRpnInById(id int64) (ret RpnIn, err error) { | |||
| &ret.Id, &ret.Leanwork, &ret.Order_id, | |||
| &ret.Order_time, &ret.Order_amount, &ret.Deal_id, | |||
| &ret.Deal_time, &ret.Pay_amount, &ret.Pay_result, | |||
| &ret.Signature, &ret.Ts) | |||
| &ret.Signature, &ret.Ts, &ret.Ip4) | |||
| if err != nil { | |||
| if err == sql.ErrNoRows { | |||
| log.Println("trying to retrieve rpnIn(" + strconv.FormatInt(id, 10) + ") but not found") | |||
| @@ -46,27 +48,27 @@ func getRpnInById(id int64) (ret RpnIn, err error) { | |||
| } | |||
| //add to database | |||
| func (m *RpnIn) add() (ret RpnIn, err error) { | |||
| func (m *RpnIn) add2db() (ret RpnIn, err error) { | |||
| if err = db.conn(Config); err != nil { | |||
| return | |||
| } | |||
| defer db.close() | |||
| q := `"INSERT INTO rpnIn( | |||
| q := `INSERT INTO rpnIn( | |||
| leanwork, order_id, order_time, order_amount, deal_id, | |||
| deal_time, pay_amount, pay_result, signature) | |||
| VALUES(?,?,?,?,?,?,?,?,?) | |||
| ` | |||
| insForm, err := db.h.Prepare(q) | |||
| if err != nil { | |||
| log.Printf("Failed to prepare SQL statment for insert") | |||
| log.Printf("Failed to prepare SQL statment for insert " + err.Error()) | |||
| return | |||
| } | |||
| res, err := insForm.Exec( | |||
| m.Leanwork, m.Order_id, m.Order_time, m.Order_amount, m.Deal_id, | |||
| m.Deal_time, m.Pay_amount, m.Pay_result, m.Signature) | |||
| if err != nil { | |||
| log.Printf("Error inserting rpnIn with orderNo =%s \n", m.Order_id) | |||
| log.Printf("Error inserting rpnIn with orderNo =%s, %s \n", m.Order_id, err.Error()) | |||
| return | |||
| } | |||
| id, err := res.LastInsertId() | |||
| @@ -75,7 +77,12 @@ func (m *RpnIn) add() (ret RpnIn, err error) { | |||
| return | |||
| } | |||
| return getRpnInById(id) | |||
| ret, err = getRpnInById(id) | |||
| if err == nil { | |||
| *m = ret | |||
| } | |||
| return | |||
| } | |||
| func (m *RpnIn) signature() string { | |||
| @@ -86,6 +93,51 @@ func (m *RpnIn) signature() string { | |||
| s += "|deal_time=" + m.Deal_time | |||
| s += "|pay_amount=" + m.Pay_amount | |||
| s += "|pay_result=" + m.Pay_result | |||
| s += "|key=" + m.md5key() | |||
| return md5str(s) | |||
| } | |||
| func (m *RpnIn) md5key() string { | |||
| if m.Order_id == "" { | |||
| return "" | |||
| } | |||
| ro, err := getRpnOutByOrderId(m.Order_id) | |||
| if err != nil { | |||
| log.Println("Cannot get RpnOut by order_id=" + m.Order_id) | |||
| return "" | |||
| } | |||
| return ro.getMD5Key() | |||
| } | |||
| return s | |||
| func GetRpnInFromHTTPRequest(r *http.Request) (ret RpnIn, err error) { | |||
| r.ParseForm() | |||
| ret.Order_id = r.FormValue("order_id") | |||
| ret.Order_time = r.FormValue("order_time") | |||
| ret.Order_amount = r.FormValue("order_amount") | |||
| ret.Deal_id = r.FormValue("deal_id") | |||
| ret.Deal_time = r.FormValue("deal_time") | |||
| ret.Pay_amount = r.FormValue("pay_amount") | |||
| ret.Pay_result = r.FormValue("pay_result") | |||
| ret.Signature = r.FormValue("signature") | |||
| ret.Ip4 = getClientIPLong(r) | |||
| ret.Ts = time.Now() | |||
| if ret.Order_id == "" { | |||
| err = errors.New("Invalid OrderId for RpnIn") | |||
| return | |||
| } | |||
| ro, err := getRpnOutByOrderId(ret.Order_id) | |||
| if err != nil || ro.Order_id != ret.Order_id { | |||
| log.Println("Cannot get RpnOut by order_id=" + ret.Order_id) | |||
| return | |||
| } | |||
| if ret.Signature != ret.signature() { | |||
| log.Println("Invalid RpnIn Signature") | |||
| err = errors.New("Invalid signauture for RpnIn") | |||
| return | |||
| } | |||
| return | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| package main | |||
| import ( | |||
| "io/ioutil" | |||
| "net/http" | |||
| "net/url" | |||
| "testing" | |||
| "time" | |||
| ) | |||
| func TestRpnInCall(t *testing.T) { | |||
| form := url.Values{} | |||
| ri := RpnIn{} | |||
| ri.Order_id = "200310160057TW002184000000000001" | |||
| ri.Order_time = time.Now().Format("20060102150405") | |||
| ri.Order_amount = "120000" | |||
| ri.Deal_id = "deal_id_demo_01" | |||
| ri.Deal_time = time.Now().Format("20060102150405") | |||
| ri.Pay_amount = "120000" | |||
| ri.Pay_result = "3" //3 success, 1 processing | |||
| ri.Signature = ri.signature() | |||
| form.Set("order_id", ri.Order_id) | |||
| form.Set("order_time", ri.Order_time) | |||
| form.Set("order_amount", ri.Order_amount) | |||
| form.Set("deal_id", ri.Deal_id) | |||
| form.Set("deal_time", ri.Deal_time) | |||
| form.Set("pay_amount", ri.Pay_amount) | |||
| form.Set("pay_result", ri.Pay_result) | |||
| form.Set("signature", ri.Signature) | |||
| resp, err := http.PostForm(Config.Rpn.UrlCallBack, form) | |||
| if err != nil { | |||
| t.Error("response should have no error") | |||
| } | |||
| defer resp.Body.Close() | |||
| if resp.StatusCode != http.StatusOK { | |||
| t.Error("response should be OK 200") | |||
| } | |||
| bodyBytes, err := ioutil.ReadAll(resp.Body) | |||
| if err != nil { | |||
| t.Error("response Body cannot be read") | |||
| } | |||
| bodyString := string(bodyBytes) | |||
| if bodyString != "[SUCCESS]" { | |||
| t.Error("we expect [SUCCESS] but we received:" + bodyString) | |||
| } | |||
| } | |||
| @@ -0,0 +1,138 @@ | |||
| package main | |||
| import ( | |||
| "database/sql" | |||
| "log" | |||
| "math" | |||
| "net/http" | |||
| "net/url" | |||
| "strconv" | |||
| "time" | |||
| "github.com/go-sql-driver/mysql" | |||
| ) | |||
| type RpnOut struct { | |||
| Version string | |||
| Sign_type string | |||
| Mid string | |||
| Notify_url string | |||
| Order_amount string | |||
| Order_time string //YYYYMMDDHHMMSS | |||
| Order_id string | |||
| User_id string | |||
| User_name string | |||
| User_cardno string | |||
| Signature string | |||
| //template | |||
| Url string //where to post entire data structure | |||
| //database specific | |||
| Id int64 | |||
| Leanwork int64 | |||
| Ip4 uint32 | |||
| Ip4location sql.NullString | |||
| Ts mysql.NullTime | |||
| } | |||
| func (m *RpnOut) buildReqByLeanworkRequestP2P(row LeanworkRequest, user_name string, user_cardno string) RpnOut { | |||
| m.Version = "1.1" | |||
| m.Sign_type = "MD5" | |||
| m.Mid = Config.Rpn.MIDP2P | |||
| m.Notify_url = Config.Rpn.UrlCallBack | |||
| m.Order_id = row.OrderNo | |||
| m.Order_amount = m.translateAmountFromLeanwork(row.OrderAmount) | |||
| m.Order_time = m.now() | |||
| m.User_id = row.CustomerId | |||
| m.User_name = user_name | |||
| m.User_cardno = user_cardno | |||
| m.Signature = md5RpnFormP2P(*m) | |||
| m.Leanwork = row.Id | |||
| return *m | |||
| } | |||
| func (m *RpnOut) buildReqByLeanworkRequestFAT(row LeanworkRequest, user_name string, user_cardno string) RpnOut { | |||
| m.Version = "1.1" | |||
| m.Sign_type = "MD5" | |||
| m.Mid = Config.Rpn.MIDFAT | |||
| m.Notify_url = Config.Rpn.UrlCallBack | |||
| m.Order_id = row.OrderNo | |||
| m.Order_amount = m.translateAmountFromLeanwork(row.OrderAmount) | |||
| m.Order_time = m.now() | |||
| m.User_id = row.CustomerId | |||
| m.User_name = user_name | |||
| m.User_cardno = user_cardno | |||
| m.Signature = md5RpnFormFAT(*m) | |||
| m.Leanwork = row.Id | |||
| return *m | |||
| } | |||
| //from 0.1 to 10 cents | |||
| func (m *RpnOut) translateAmountFromLeanwork(from string) string { | |||
| f, _ := strconv.ParseFloat(from, 32) | |||
| f = f * 100 //convert to cents | |||
| t := int(math.Ceil(f)) | |||
| s := strconv.Itoa(t) | |||
| return s | |||
| } | |||
| func (m *RpnOut) now() string { | |||
| t := time.Now() | |||
| return t.Format("20060102150405") | |||
| } | |||
| //encode without disturbing it's original order | |||
| func (m *RpnOut) encode() string { | |||
| s := "version=" + m.Version | |||
| s += "&sign_type=" + m.Sign_type | |||
| s += "&mid=" + m.Mid | |||
| s += "¬ify_url=" + url.QueryEscape(m.Notify_url) | |||
| s += "&order_id=" + m.Order_id | |||
| s += "&order_amount=" + m.Order_amount | |||
| s += "&order_time=" + m.Order_time | |||
| s += "&user_id=" + m.User_id | |||
| s += "&user_name=" + url.QueryEscape(m.User_name) | |||
| s += "&user_cardno=" + m.User_cardno | |||
| s += "&signature=" + m.Signature | |||
| return s | |||
| } | |||
| func (m *RpnOut) sendRedirect(w http.ResponseWriter, row LeanworkRequest) { | |||
| //execute redirect | |||
| m.Url = Config.Rpn.Url | |||
| tmpl.ExecuteTemplate(w, "rpnCallOutRedirect", *m) | |||
| } | |||
| func (m *RpnOut) getMD5Key() string { | |||
| if m.Mid == Config.Rpn.MIDP2P { | |||
| return Config.Rpn.MD5P2P | |||
| } else if m.Mid == Config.Rpn.MD5FAT { | |||
| return Config.Rpn.MD5FAT | |||
| } else { | |||
| return "" | |||
| } | |||
| } | |||
| func getRpnOutByOrderId(order_id string) (ret RpnOut, err error) { | |||
| if err = db.conn(Config); err != nil { | |||
| return | |||
| } | |||
| defer db.close() | |||
| q := "SELECT * FROM rpnOut WHERE order_id = ? ORDER BY id DESC LIMIT 1" | |||
| err = db.h.QueryRow(q, order_id).Scan( | |||
| &ret.Id, &ret.Leanwork, &ret.Version, | |||
| &ret.Sign_type, &ret.Mid, &ret.Notify_url, | |||
| &ret.Order_amount, &ret.Order_time, &ret.Order_id, | |||
| &ret.User_id, &ret.User_name, &ret.User_cardno, &ret.Signature, | |||
| &ret.Ip4, &ret.Ip4location, &ret.Ts) | |||
| if err != nil { | |||
| if err == sql.ErrNoRows { | |||
| log.Println("trying to retrieve rpnOut(order_id=" + order_id + ") but not found") | |||
| } else { | |||
| log.Println("Error retrieving rpnOut(order_id=" + order_id + ") encountered : " + err.Error()) | |||
| } | |||
| } | |||
| return | |||
| } | |||