Skip to content

Commit

Permalink
Added refunds + feed options
Browse files Browse the repository at this point in the history
  • Loading branch information
koen-serry committed Apr 17, 2024
1 parent c8522cd commit 125d4aa
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 34 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@ Once signed, a webhook is sent (see below) after which you can fetch the detail
think of as reading out a queue. Since it'll return you the changes since the last time you called it.

```go
err := c.DocumentFeed(context.Background(), func(mandate *Mndt, eventTime string) {
err := c.DocumentFeed(context.Background(), func(mandate *Mndt, eventTime string, eventId int64) {
fmt.println("Document created ", mandate.MndtId, " @ ", eventTime)
}, func(originalMandateNumber string, mandate *Mndt, reason *AmdmntRsn, eventTime string) {
}, func(originalMandateNumber string, mandate *Mndt, reason *AmdmntRsn, eventTime string, eventId int64) {
fmt.println("Document updated ", originalMandateNumber, reason.Rsn, " @ ", eventTime)
}, func(mandateNumber string, reason *CxlRsn, eventTime string) {
}, func(mandateNumber string, reason *CxlRsn, eventTime string, eventId int64) {
fmt.println("Document cancelled ", mandateNumber, reason.Rsn, " @ ", eventTime)
})
```
Expand Down
34 changes: 26 additions & 8 deletions document.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
Expand Down Expand Up @@ -205,6 +206,7 @@ type MandateUpdate struct {
AmdmntRsn *AmdmntRsn `json:",omitempty"`
CxlRsn *CxlRsn `json:",omitempty"`
OrgnlMndtId string
EvtId int64
EvtTime string
}

Expand Down Expand Up @@ -284,7 +286,7 @@ func (c *Client) DocumentCancel(ctx context.Context, mandate string, reason stri

c.Debug.Debugf("Cancelled document %s : %s", mandate, reason)

req, _ := http.NewRequestWithContext(ctx, "DELETE", c.BaseURL+"/creditor/mandate?"+params.Encode(), nil)
req, _ := http.NewRequestWithContext(ctx, http.MethodDelete, c.BaseURL+"/creditor/mandate?"+params.Encode(), nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Accept", "application/json")
Expand Down Expand Up @@ -315,21 +317,37 @@ func (c *Client) DocumentSuspend(ctx context.Context, mandate string, suspend bo
// DocumentFeed retrieves all documents since the last call with callbacks since there may be many
func (c *Client) DocumentFeed(
ctx context.Context,
newDocument func(mandate *Mndt, eventTime string),
updateDocument func(originalMandateNumber string, mandate *Mndt, reason *AmdmntRsn, eventTime string),
cancelledDocument func(mandateNumber string, reason *CxlRsn, eventTime string)) error {
newDocument func(mandate *Mndt, eventTime string, eventId int64),
updateDocument func(originalMandateNumber string, mandate *Mndt, reason *AmdmntRsn, eventTime string, eventId int64),
cancelledDocument func(mandateNumber string, reason *CxlRsn, eventTime string, eventId int64),
options ...FeedOption) error {

if err := c.refreshTokenIfRequired(); err != nil {
return err
}

feedOptions := parseFeedOptions(options)
_url := c.BaseURL + "/creditor/mandate"
for i, sideload := range feedOptions.includes {
if i == 0 {
_url = _url + "?include=" + sideload
} else {
_url = _url + "&include=" + sideload
}
}

for {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, c.BaseURL+"/creditor/mandate", nil)
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, _url, nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Accept", "application/json")
req.Header.Set("User-Agent", c.UserAgent)

if feedOptions.start != -1 {
req.Header.Set("X-RESUME-AFTER", fmt.Sprintf("%d", feedOptions.start))
feedOptions.start = -1
}

res, err := c.HTTPClient.Do(req)
if err != nil {
return err
Expand All @@ -346,11 +364,11 @@ func (c *Client) DocumentFeed(
c.Debug.Debugf("Fetched %d documents\n", len(updates.Messages))
for _, update := range updates.Messages {
if update.CxlRsn != nil {
cancelledDocument(update.OrgnlMndtId, update.CxlRsn, update.EvtTime)
cancelledDocument(update.OrgnlMndtId, update.CxlRsn, update.EvtTime, update.EvtId)
} else if update.AmdmntRsn != nil {
updateDocument(update.OrgnlMndtId, update.Mndt, update.AmdmntRsn, update.EvtTime)
updateDocument(update.OrgnlMndtId, update.Mndt, update.AmdmntRsn, update.EvtTime, update.EvtId)
} else {
newDocument(update.Mndt, update.EvtTime)
newDocument(update.Mndt, update.EvtTime, update.EvtId)
}
}

Expand Down
6 changes: 3 additions & 3 deletions document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ func TestDocumentFeed(t *testing.T) {
}

t.Run("DocumentFeed", func(t *testing.T) {
err := c.DocumentFeed(context.Background(), func(mandate *Mndt, eventTime string) {
err := c.DocumentFeed(context.Background(), func(mandate *Mndt, eventTime string, eventId int64) {
t.Log("Document created ", mandate.MndtId, " @ ", eventTime)
}, func(originalMandateNumber string, mandate *Mndt, reason *AmdmntRsn, eventTime string) {
}, func(originalMandateNumber string, mandate *Mndt, reason *AmdmntRsn, eventTime string, eventId int64) {
t.Log("Document updated ", originalMandateNumber, reason.Rsn, " @ ", eventTime)
}, func(mandateNumber string, reason *CxlRsn, eventTime string) {
}, func(mandateNumber string, reason *CxlRsn, eventTime string, eventId int64) {
t.Log("Document cancelled ", mandateNumber, reason.Rsn, " @ ", eventTime)
})
if err != nil {
Expand Down
77 changes: 77 additions & 0 deletions example/feed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package main

import (
"context"
"fmt"
"github.com/twikey/twikey-api-go"
"os"
"time"
)

func main() {
apikey := os.Getenv("TWIKEY_API_KEY")
apiurl := os.Getenv("TWIKEY_API_URL")
if apikey == "" {
fmt.Println("TWIKEY_API_KEY environment variable not set")
os.Exit(1)
}
client := twikey.NewClient(apikey,
twikey.WithBaseURL(apiurl))

now := time.Now()
filename_as_date := now.Format("2006-01-02T15:04:05Z")

file, err := os.OpenFile(fmt.Sprintf("%s.csv", filename_as_date), os.O_CREATE|os.O_RDWR, 0666)

ctx := context.Background()
err = client.DocumentFeed(ctx,
func(mandate *twikey.Mndt, eventTime string, eventId int64) {
_, _ = fmt.Fprintf(file, "%s;mandate;new;%d;%s;%s\n", eventTime, eventId, mandate.MndtId, mandate.DbtrAcct)
},
func(originalMandateNumber string, mandate *twikey.Mndt, reason *twikey.AmdmntRsn, eventTime string, eventId int64) {
_, _ = fmt.Fprintf(file, "%s;mandate;update;%d;%s;%s\n", eventTime, eventId, mandate.MndtId, reason.Rsn)
},
func(mandateNumber string, reason *twikey.CxlRsn, eventTime string, eventId int64) {
_, _ = fmt.Fprintf(file, "%s;mandate;cancel;%d;%s;%s\n", eventTime, eventId, mandateNumber, reason.Rsn)
},
twikey.FeedInclude("seq"), twikey.FeedStartPosition(0))
if err != nil {
panic(err)
}

eventTime := time.Now().Format("2006-01-02T15:04:05Z")
err = client.TransactionFeed(ctx,
func(transaction *twikey.Transaction) {
_, _ = fmt.Fprintf(file, "%s;transaction;update;%d;%s;%s;%s;%s\n", eventTime, transaction.Seq, transaction.BookedDate, transaction.DocumentReference, transaction.Ref, transaction.State)
},
twikey.FeedInclude("seq"), twikey.FeedStartPosition(0))
if err != nil {
panic(err)
}

eventTime = time.Now().Format("2006-01-02T15:04:05Z")
err = client.PaylinkFeed(ctx,
func(paylink *twikey.Paylink) {
_, _ = fmt.Fprintf(file, "%s;paylink;update;%d;%d;%s;%s\n", eventTime, paylink.Seq, paylink.Id, paylink.Ref, paylink.State)
},
twikey.FeedInclude("seq"), twikey.FeedStartPosition(0))
if err != nil {
panic(err)
}

eventTime = time.Now().Format("2006-01-02T15:04:05Z")
err = client.RefundFeed(ctx,
func(refund *twikey.Refund) {
_, _ = fmt.Fprintf(file, "%s;refund;update;%d;%s;%s;%s\n", eventTime, refund.Seq, refund.Id, refund.Ref, refund.State)
},
twikey.FeedInclude("seq"), twikey.FeedStartPosition(0))
if err != nil {
panic(err)
}

file.Close()
info, err := os.Lstat(file.Name())
if info.Size() == 0 {
os.Remove(file.Name())
}
}
35 changes: 21 additions & 14 deletions invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
Expand Down Expand Up @@ -232,52 +233,58 @@ func (c *Client) InvoiceAdd(ctx context.Context, invoiceRequest *NewInvoiceReque
}

// InvoiceFeed Get invoice Feed twikey
func (c *Client) InvoiceFeed(ctx context.Context, callback func(invoice *Invoice), sideloads ...string) error {
func (c *Client) InvoiceFeed(ctx context.Context, callback func(invoice *Invoice), options ...FeedOption) error {

if err := c.refreshTokenIfRequired(); err != nil {
return err
}

feedOptions := parseFeedOptions(options)
_url := c.BaseURL + "/creditor/invoice"
for i, sideload := range sideloads {
for i, sideload := range feedOptions.includes {
if i == 0 {
_url = _url + "?include=" + sideload
} else {
_url = _url + "&include=" + sideload
}
}

req, _ := http.NewRequestWithContext(ctx, http.MethodGet, _url, nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Authorization", c.apiToken) //Already there
req.Header.Set("User-Agent", c.UserAgent)
for {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, _url, nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("User-Agent", c.UserAgent)

var feeds InvoiceFeed
var moreInvoices = true
if feedOptions.start != -1 {
req.Header.Set("X-RESUME-AFTER", fmt.Sprintf("%d", feedOptions.start))
feedOptions.start = -1
}

for moreInvoices {
var feeds InvoiceFeed
if err := c.sendRequest(req, &feeds); err != nil {
return err
}

for _, invoice := range feeds.Invoices {
callback(&invoice)
}

moreInvoices = len(feeds.Invoices) >= 100
if len(feeds.Invoices) == 0 {
return nil
}
}
return nil
}

// InvoiceDetail allows a snapshot of a particular invoice, note that this is rate limited
func (c *Client) InvoiceDetail(ctx context.Context, invoiceIdOrNumber string, sideloads ...string) (*Invoice, error) {
func (c *Client) InvoiceDetail(ctx context.Context, invoiceIdOrNumber string, feedOptions ...FeedOption) (*Invoice, error) {

if err := c.refreshTokenIfRequired(); err != nil {
return nil, err
}

feedOption := parseFeedOptions(feedOptions)

_url := c.BaseURL + "/creditor/invoice/" + invoiceIdOrNumber
for i, sideload := range sideloads {
for i, sideload := range feedOption.includes {
if i == 0 {
_url = _url + "?include=" + sideload
} else {
Expand Down
2 changes: 1 addition & 1 deletion invoice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestInvoiceFeed(t *testing.T) {
}

t.Logf("Invoice update with number %s %.2f euro %s", invoice.Number, invoice.Amount, newState)
}, "lastpayment", "meta", "customer")
}, FeedInclude("lastpayment", "meta", "customer"))
if err != nil {
t.Error(err)
}
Expand Down
10 changes: 8 additions & 2 deletions paylink.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func (request *PaylinkRequest) Add(key string, value string) {
// Paylink is the response receiving from Twikey upon a request
type Paylink struct {
Id int64 `json:"id,omitempty"`
Seq int64 `json:"seq,omitempty"`
Amount float64 `json:"amount,omitempty"`
Msg string `json:"msg,omitempty"`
Ref string `json:"ref,omitempty"`
Expand Down Expand Up @@ -103,14 +104,15 @@ func (c *Client) PaylinkNew(ctx context.Context, paylinkRequest *PaylinkRequest)
}

// PaylinkFeed retrieves the feed of updated paylinks since last call
func (c *Client) PaylinkFeed(ctx context.Context, callback func(paylink *Paylink), sideloads ...string) error {
func (c *Client) PaylinkFeed(ctx context.Context, callback func(paylink *Paylink), options ...FeedOption) error {

if err := c.refreshTokenIfRequired(); err != nil {
return err
}

feedOptions := parseFeedOptions(options)
_url := c.BaseURL + "/creditor/payment/link/feed"
for i, sideload := range sideloads {
for i, sideload := range feedOptions.includes {
if i == 0 {
_url = _url + "?include=" + sideload
} else {
Expand All @@ -123,6 +125,10 @@ func (c *Client) PaylinkFeed(ctx context.Context, callback func(paylink *Paylink
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Authorization", c.apiToken)
req.Header.Set("User-Agent", c.UserAgent)
if feedOptions.start != -1 {
req.Header.Set("X-RESUME-AFTER", fmt.Sprintf("%d", feedOptions.start))
feedOptions.start = -1
}

res, err := c.HTTPClient.Do(req)
if err != nil {
Expand Down
Loading

0 comments on commit 125d4aa

Please sign in to comment.