Skip to content

Commit

Permalink
Merge pull request #16 from hossein-225/saderat
Browse files Browse the repository at this point in the history
feature: add saderat bank gateway
  • Loading branch information
hossein-225 authored Sep 14, 2024
2 parents d8777c8 + 01d0421 commit 495622b
Show file tree
Hide file tree
Showing 9 changed files with 502 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ This library simplifies the process of integrating with Iranian bank payment gat
- [Bitpay Documentation](./gateways/bitpay/README_BitPay.md)
- [Zarinpal Documentation](./gateways/zarinpal/README_Zarinpal.md)
- [Saman Bank Documentation](./gateways/saman/README_Saman.md)
- [Saderat Bank Documentation](./gateways/saderat/README_Saderat.md)


## Get Started

Expand Down
8 changes: 8 additions & 0 deletions configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,19 @@ type ZarinpalConfig struct {
PayURL string `mapstructure:"payURL"`
}

type SaderatConfig struct {
RequestURL string `mapstructure:"requestURL"`
PayURL string `mapstructure:"payURL"`
AdviseURL string `mapstructure:"adviseURL"`
RollBackURL string `mapstructure:"rollBackURL"`
}

type Config struct {
BitPay BitPayConfig `mapstructure:"bitpay"`
Mellat MellatConfig `mapstructure:"mellat"`
Saman SamanConfig `mapstructure:"saman"`
Zarinpal ZarinpalConfig `mapstructure:"zarinpal"`
Saderat SaderatConfig `mapstructure:"saderat"`
}

var AppConfig Config
Expand Down
8 changes: 7 additions & 1 deletion configs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ zarinpal:
payURL: "https://www.zarinpal.com/pg/StartPay/"
verifyURL: "https://api.zarinpal.com/pg/v4/payment/verify.json"
inquiryURL: "https://api.zarinpal.com/pg/v4/payment/inquiry.json"
unverifiedURL: "https://api.zarinpal.com/pg/v4/payment/unVerified.json"
unverifiedURL: "https://api.zarinpal.com/pg/v4/payment/unVerified.json"

saderat:
TokenURL: "https://sepehr.shaparak.ir:8081/V1/PeymentApi/GetToken"
PaymentURL: "https://mabna.shaparak.ir:8080/Pay"
AdviceURL: "https://mabna.shaparak.ir:8082/ipg.svc?wsdl"
RollbackURL: "https://mabna.shaparak.ir:8082/ipg.svc?wsdl"
173 changes: 173 additions & 0 deletions gateways/saderat/README_Saderat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Saderat Gateway Integration

This Go package provides a simple way to integrate the Saderat payment gateway into your Golang application. It allows you to send payment requests and advise transactions, and more using the Saderat Bank Payment Gateway API.

## Requirements

- **Terminal ID**: Provided by Saderat Bank.

## Installation

To install the package, use `go get`:

```bash
go get github.com/hossein-225/Iranian-bank-gateways/gateways/saderat
```

## Usage

### 1. Initialize the Saderat Payment Service

You need to initialize the `PaymentService` service by passing your Terminal ID.

```go
import "github.com/hossein-225/Iranian-bank-gateways/gateways/saderat"

func main() {
service, err := saderat.NewPaymentService("your-terminal-id")
if err != nil {
log.Fatalf("Failed to initialize Saderat payment service: %v", err)
}
}
```

### 2. Send a Payment Request

To send a payment request, use the `SendRequest` method. The method requires the amount,redirect URL, InvoiceID and Additional data under the Payload field.

```go
paymentToken, err := service.SendRequest(context.Background(), 100000, "https://your-callback-url.com", "123456", "payload")
if err != nil {
log.Fatalf("Failed to request paymentToken: %v", err)
}

fmt.Printf("Your payment token: %v\n", paymentToken)
```

After a successful request, you will receive a payment token that the user needs to be redirected to in order to complete the transaction. Once the paymentToken is received, you need to construct an HTML form to redirect the user to the payment gateway. The form should be sent using the POST method, and it must include both the TerminalID and the paymentToken.

```html
<form
id="paymentTokenForm"
action="https://mabna.shaparak.ir:8080/Pay"
method="post"
>
<input type="hidden" name="TerminalID" value="your-Terminal-id" />
<input type="hidden" name="token" value="your-Payment-Token" />
</form>
<script type="text/javascript">
document.getElementById("paymentTokenForm").submit();
</script>
```

TerminalID: This is the Terminal ID provided by the bank, which identifies your merchant account.

Token: This is the payment token returned from the SendRequest method.

The form is automatically submitted by the JavaScript code, redirecting the user to the payment gateway page where they can finalize the payment. You need to replace your-Terminal-id and your-Payment-Token with actual values in your application.

### 3. advise a Transaction

Once the user completes the payment, Saderat will redirect them to your callback URL with transaction parameters. You can then advise the transaction using the `ConfirmTransaction` method.

```go
adviseResponse, err := service.ConfirmTransaction(context.Background(), "digitalReceipt", 100000)
if err != nil {
log.Fatalf("Transaction verification failed: %v", err)
}

fmt.Printf("Verification result: %v", adviseResponse)
```

### 4. rollBack a Transaction

If you need to rollBack a transaction (e.g., if the transaction is not completed), you can use the `RollbackTransaction` method.

```go
rollBackResponse, err := service.RollbackTransaction(context.Background(), "digitalReceipt", 100000)
if err != nil {
log.Fatalf("Transaction reversal failed: %v\n", err)
}

fmt.Printf("Reversal result: %v\n", rollBackResponse)
```

## Example

Here is a full example of requesting, verifying, and reversing a payment:

```go
package main

import (
"context"
"fmt"
"log"

"github.com/hossein-225/Iranian-bank-gateways/gateways/saderat"

)

func main() {
// Initialize Saderat payment service
service, err := saderat.NewPaymentService("your-terminal-id")
if err != nil {
log.Fatalf("Failed to initialize Saderat payment service: %v", err)
}


// Send a payment request and get the payment token
paymentToken, err := service.SendRequest(context.Background(), 100000, "https://your-callback-url.com", "123456", "payload")
if err != nil {
log.Fatalf("Failed to request paymentToken: %v", err)
}

fmt.Printf("Your payment token: %v\n", paymentToken)

/* After successfully receiving the paymentToken from the SendRequest method,
you should generate and inject an HTML form into the response that includes
the TerminalID and paymentToken, and use JavaScript to automatically submit
the form. This will redirect the user to the payment gateway page to complete the transaction
*/

fmt.Printf(`
<form id="paymentTokenForm" action="https://mabna.shaparak.ir:8080/Pay" method="post">
<input type="hidden" name="TerminalID" value="your-terminal-id"/>
<input type="hidden" name="token" value="%s"/>
</form>
<script type="text/javascript">
document.getElementById('paymentTokenForm').submit();
</script>
`, paymentToken)

// Advise the transaction
adviseResponse, err := service.ConfirmTransaction(context.Background(), "digitalReceipt", 100000)
if err != nil {
log.Fatalf("Transaction verification failed: %v", err)
}

fmt.Printf("Verification result: %v", adviseResponse)

// RollBack the transaction if necessary
rollBackResponse, err := service.RollbackTransaction(context.Background(), "digitalReceipt", 100000)
if err != nil {
log.Fatalf("Transaction reversal failed: %v\n", err)
}

fmt.Printf("Reversal result: %v\n", rollBackResponse)

}
```

## Error Handling

Errors are handled internally by the package. If a request fails, the error message will be returned, and you can handle it accordingly in your application.

## License

This package is open-source and is licensed under the [MIT License](https://opensource.org/licenses/MIT).

## Notes

- Ensure that your IP address and callback URL are properly configured with Saderat Bank to allow access to their API.
- Make sure that you handle the transaction digital receipts and response codes securely.
56 changes: 56 additions & 0 deletions gateways/saderat/advise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package saderat

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"

"github.com/hossein-225/Iranian-bank-gateways/internal/errors"
"go.uber.org/zap"

config "github.com/hossein-225/Iranian-bank-gateways/configs"
neturl "net/url"
)

func (ps *PaymentService) ConfirmTransaction(ctx context.Context, digitalReceipt string, amount int64) (*AdviceResponse, error) {
data := neturl.Values{}
data.Set("Tid", strconv.FormatInt(ps.TerminalID, 10))
data.Set("digitalreceipt", digitalReceipt)

req, err := http.NewRequestWithContext(ctx, http.MethodPost, config.AppConfig.Saderat.AdviseURL, strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
ps.Logger.Error("failed to close response body", zap.Error(closeErr))
}
}()

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

var result AdviceResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}

if int64(result.ReturnID) != amount {
return nil, fmt.Errorf("خطا در تایید: %s", errors.HandleServiceErrors(result.ReturnID))
}

return &result, nil
}
65 changes: 65 additions & 0 deletions gateways/saderat/pay_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package saderat

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/hossein-225/Iranian-bank-gateways/internal/errors"
"go.uber.org/zap"

config "github.com/hossein-225/Iranian-bank-gateways/configs"
)

func (ps *PaymentService) SendRequest(ctx context.Context, amount int64, callbackURL, invoiceID, payload string) (string, error) {
paymentRequest := PaymentRequest{
TerminalID: ps.TerminalID,
Amount: amount,
CallbackURL: callbackURL,
InvoiceID: invoiceID,
Payload: payload,
}

jsonData, err := json.Marshal(paymentRequest)
if err != nil {
return "", fmt.Errorf("خطا در تبدیل درخواست به JSON: %w", err)
}

req, err := http.NewRequestWithContext(ctx, http.MethodPost, config.AppConfig.Saderat.RequestURL, bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("خطا در ساخت درخواست HTTP: %w", err)
}

req.Header.Set("Content-Type", "application/json")
client := &http.Client{}

resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("خطا در ارسال درخواست HTTP: %w", err)
}

defer func() {
if err := resp.Body.Close(); err != nil {
ps.Logger.Error("Error closing response body:", zap.Error(err))
}
}()

if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("خطا: %s", errors.HandleServiceErrors(resp.StatusCode))
}

var responseMap map[string]any
if err := json.NewDecoder(resp.Body).Decode(&responseMap); err != nil {
return "", fmt.Errorf("خطا در پردازش پاسخ: %w", err)
}

accessToken, ok := responseMap["AccessToken"].(string)

if !ok {
return "", fmt.Errorf("خطا: %s", errors.HandleServiceErrors(10))
}

return accessToken, nil
}
Loading

0 comments on commit 495622b

Please sign in to comment.