// Go 1.20+, standard library only. Run on the store server. package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/url" "os" "regexp" "strings" "time" ) type DidbanClient struct { baseURL, key string; http *http.Client } type Invoice struct { ID string `json:"id"` OrderID string `json:"order_id"` AmountToman string `json:"amount_toman"` CardNumber string `json:"card_number"` Cardholder string `json:"cardholder"` ExpiresAt string `json:"expires_at"` ExpiresInSeconds int64 `json:"expires_in_seconds"` Mode string `json:"mode"` AmountRial string `json:"amount_rial"` Status string `json:"status"` PaymentURL string `json:"payment_url"` Paid bool `json:"paid"` RequiresReview bool `json:"requires_review"` TestPayment bool `json:"test_payment"` } func NewDidbanClient(base,key string) (*DidbanClient,error) { u,err:=url.Parse(base) if err!=nil || u.Hostname()=="" || u.User!=nil || u.RawQuery!="" || u.Fragment!="" || (u.Path!="" && u.Path!="/") || !(u.Scheme=="https" || u.Scheme=="http" && (u.Hostname()=="localhost" || u.Hostname()=="127.0.0.1")) { return nil,fmt.Errorf("use an HTTPS origin or localhost") } if !regexp.MustCompile(`^db_(test|live)_[A-Za-z0-9_-]{43}$`).MatchString(key) { return nil,fmt.Errorf("invalid API key") } return &DidbanClient{strings.TrimRight(base,"/"),key,&http.Client{Timeout:20*time.Second,CheckRedirect:func(req *http.Request,via []*http.Request) error { return http.ErrUseLastResponse }}},nil } func (c *DidbanClient) request(method,path string,body any) (Invoice,error) { var out Invoice;var payload []byte if body!=nil { var err error;payload,err=json.Marshal(body);if err!=nil { return out,err } } req,err:=http.NewRequest(method,c.baseURL+path,bytes.NewReader(payload));if err!=nil { return out,err } req.Header.Set("Authorization","Bearer "+c.key);req.Header.Set("Content-Type","application/json");req.Header.Set("Accept","application/json") res,err:=c.http.Do(req);if err!=nil { return out,fmt.Errorf("connection failed; retry with the same request_id") };defer res.Body.Close() if res.StatusCode<200 || res.StatusCode>=300 { return out,fmt.Errorf("Didban HTTP %d",res.StatusCode) } err=json.NewDecoder(io.LimitReader(res.Body,1024*1024)).Decode(&out);return out,err } func (c *DidbanClient) CreateInvoice(requestID,amountRial string) (Invoice,error) { return c.request("POST","/api/v1/invoices",map[string]any{"request_id":requestID,"amount_rial":amountRial,"ttl_minutes":30}) } func (c *DidbanClient) GetInvoice(id string) (Invoice,error) { if !regexp.MustCompile(`^[0-9a-f-]{36}$`).MatchString(id) { return Invoice{},fmt.Errorf("invalid invoice ID") };return c.request("GET","/api/v1/invoices/"+id,nil) } func (c *DidbanClient) CreateDirectInvoice(requestID,amountRial string) (Invoice,error) { return c.request("POST","/api/v1/invoices",map[string]any{"request_id":requestID,"amount_rial":amountRial,"presentation":"direct"}) } func (c *DidbanClient) GetPaymentData(id string) (Invoice,error) { if !regexp.MustCompile(`^[0-9a-f-]{36}$`).MatchString(id) { return Invoice{},fmt.Errorf("invalid invoice ID") };return c.request("GET","/api/v1/invoices/"+id+"/payment-data",nil) } func main() { base:=os.Getenv("DIDBAN_API_URL");if base=="" { base="http://127.0.0.1:4321" } client,err:=NewDidbanClient(base,os.Getenv("DIDBAN_API_KEY"));if err!=nil { panic(err) } invoice,err:=client.CreateInvoice("GO-ORDER-1001","550680");if err!=nil { panic(err) } // Save invoice.ID beside the permanent order identifier in your database. fmt.Println("Payment URL:",invoice.PaymentURL) state,err:=client.GetInvoice(invoice.ID);if err!=nil { panic(err) };fmt.Println("Status:",state.Status,"| Requires review:",state.RequiresReview) // Only live invoices with a verified bank approval can report Paid=true. Never fulfill an order from TestPayment. }