-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathc2bExpress.go
160 lines (131 loc) · 6.5 KB
/
c2bExpress.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package darajago
// LIPA NA M-PESA ONLINE API also know as M-PESA express
// (STK Push) is a Merchant/Business initiated C2BPayload (Customer to Business) Payment.
import (
"encoding/base64"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
const (
ExpressDefaultCallBackURL = "daraja-payments/mpesa"
)
// ExpressCallBackFunc A function called after MPESA API sends a request
type ExpressCallBackFunc func(response *CallbackResponse, request *http.Request, err error)
// LipaNaMpesaPayload is used to initiate a transaction on Lipa Na M-Pesa Online Payment
type LipaNaMpesaPayload struct {
BusinessShortCode string `json:"BusinessShortCode"`
Password string `json:"Password"`
Timestamp string `json:"Timestamp"`
TransactionType string `json:"TransactionType"`
Amount string `json:"Amount"`
PartyA string `json:"PartyA"`
PartyB string `json:"PartyB"`
PhoneNumber string `json:"PhoneNumber"`
CallBackURL string `json:"CallBackURL"`
AccountReference string `json:"AccountReference"`
TransactionDesc string `json:"TransactionDesc"`
}
// LipaNaMpesaResponse represents a response from the Lipa Na Mpesa API.
type LipaNaMpesaResponse struct {
// MerchantRequestID is the unique request ID for tracking a transaction.
MerchantRequestID string `json:"MerchantRequestID"`
// CheckoutRequestID is the unique request ID for the checkout process.
CheckoutRequestID string `json:"CheckoutRequestID"`
// ResponseCode is the response code for the request.
ResponseCode string `json:"ResponseCode"`
// ResponseDescription is a description of the response.
ResponseDescription string `json:"ResponseDescription"`
// CustomerMessage is a message for the customer.
CustomerMessage string `json:"CustomerMessage"`
}
// STKPushStatusPayload is used to check the status of a transaction on Lipa Na M-Pesa Online Payment
type STKPushStatusPayload struct {
BusinessShortCode string `json:"BusinessShortCode"`
Password string `json:"Password"`
Timestamp string `json:"Timestamp"`
CheckoutRequestID string `json:"CheckoutRequestID"`
}
// STKPushStatusResponse represents a response from the Lipa Na Mpesa API.
type STKPushStatusResponse struct {
// MerchantRequestID This is a global unique Identifier for any submited payment request. String 16813-1590513-1
MerchantRequestID string `json:"MerchantRequestID"`
// CheckoutRequestID This is a global unique Identifier for the processed payment request. String ws_CO_DMZ_1234567890
CheckoutRequestID string `json:"CheckoutRequestID"`
// ResponseCode This is a Numeric status code that indicates the status of the transaction submission. 0 means successful submission and any other code means an error occured. Numeric 0\
ResponseCode string `json:"ResponseCode"`
// ResponseDescription This is a description of the response code. String Success. Request accepted for processing
ResponseDescription string `json:"ResponseDescription"`
//ResultDesc Result description is a message from the API that gives the status of the request processing, usualy maps to a specific ResultCode value. It can be a Success processing message or an error description message. String E.g: 0: The service request is processed successfully., 1032: Request cancelled by user
ResultDesc string `json:"ResultDesc"`
// ResultCode This is a numeric status code that indicates the status of the transaction processing. 0 means successful processing and any other code means an error occured or the transaction failed. Numeric 0, 1032
ResultCode string `json:"ResultCode"`
}
type CallbackResponse struct {
Body struct {
StkCallback struct {
MerchantRequestID string `json:"MerchantRequestID"`
CheckoutRequestID string `json:"CheckoutRequestID"`
ResultCode int `json:"ResultCode"`
ResultDesc string `json:"ResultDesc"`
CallbackMetadata struct {
Item []struct {
Name string `json:"Name"`
Value interface{} `json:"Value"`
} `json:"Item"`
} `json:"CallbackMetadata"`
} `json:"stkCallback"`
} `json:"Body"`
}
// MakeSTKPushRequest is a function that initiates a Lipa Na Mpesa Online payment.
// It takes in a LipaNaMpesaPayload struct representing the payment configuration,
// and returns a LipaNaMpesaResponse struct representing the response from the Lipa Na Mpesa API,
// or an ErrorResponse struct representing an error that occurred during the request.
func (d *DarajaApi) MakeSTKPushRequest(mpesaConfig LipaNaMpesaPayload) (*LipaNaMpesaResponse, *ErrorResponse) {
//timestamp
t := time.Now()
layout := "20060102150405"
timestamp := t.Format(layout)
// marshal the struct into a map
password := base64.StdEncoding.EncodeToString([]byte(mpesaConfig.BusinessShortCode + mpesaConfig.Password + timestamp))
// add the timestamp and password to the map
mpesaConfig.Timestamp = timestamp
mpesaConfig.Password = password
secureResponse, err := performSecurePostRequest[LipaNaMpesaResponse](mpesaConfig, endpointLipaNaMpesa, d)
if err != nil {
return nil, err
}
return &secureResponse.Body, nil
}
// QuerySTKPushStatus is a function that queries the status of a Lipa Na Mpesa Online payment.
// It takes in a STKPushStatusPayload struct representing the payment configuration,
// and returns a STKPushStatusResponse struct representing the response from the Lipa Na Mpesa API,
// or an ErrorResponse struct representing an error that occurred during the request.
func (d *DarajaApi) QuerySTKPushStatus(mpesaConfig STKPushStatusPayload) (*STKPushStatusResponse, *ErrorResponse) {
//timestamp
t := time.Now()
layout := "20060102150405"
timestamp := t.Format(layout)
// marshal the struct into a map
password := base64.StdEncoding.EncodeToString([]byte(mpesaConfig.BusinessShortCode + mpesaConfig.Password + timestamp))
// add the timestamp and password to the map
mpesaConfig.Timestamp = timestamp
mpesaConfig.Password = password
secureResponse, err := performSecurePostRequest[STKPushStatusResponse](mpesaConfig, endpointQueryLipanaMpesa, d)
if err != nil {
return nil, err
}
return &secureResponse.Body, nil
}
// MapExpressGinCallBack Register a callback for listening to MPESA requests
func MapExpressGinCallBack(gingroup *gin.RouterGroup, callBackUrl string, callback ExpressCallBackFunc) {
gingroup.POST(callBackUrl, func(context *gin.Context) {
var callbackResponse CallbackResponse
err := context.BindJSON(&callbackResponse)
if err != nil {
callback(nil, nil, err)
}
callback(&callbackResponse, context.Request, nil)
context.JSON(http.StatusOK, map[string]string{"status": "payment processing"})
})
}