diff --git a/README.md b/README.md index 4ea7402..8cc5bd0 100644 --- a/README.md +++ b/README.md @@ -4,28 +4,42 @@ Generate ~~and validate~~ payments of Brazil Instant Payment System (Pix), making fast and simple to handle charges and proccess then in your project. -**UNDER DEVELOPMENT** *** Not ready for production +**UNDER DEVELOPMENT** *** Becareful *** Example implementation. ```golang opts := []pix.Options{ - pix.OptPixKey("11955555555"), - pix.OptDescription("Teste"), - pix.OptMerchantName("Thiago Zilli Sarmento"), - pix.OptMerchantCity("Ararangua"), - pix.OptAmount("0.00"), - pix.OptKind(pix.STATIC), -} - -p, err := pix.New(opts...) -if err != nil { - panic(err) -} -cpy := p.GenPayload() -if err != nil { - panic(err) -} -fmt.Println(cpy) + pix.OptPixKey("11955555555"), + pix.OptDescription("Teste"), + pix.OptMerchantName("Thiago Zilli Sarmento"), + pix.OptMerchantCity("Ararangua"), + pix.OptAmount("1.00"), + pix.OptAditionalInfo("gerado por go-pixgen"), + pix.OptKind(pix.STATIC), + } + + p, err := pix.New(opts...) + if err != nil { + fmt.Println(err.Error()) + return + } + + if err := p.Validates(); err != nil { + fmt.Println(err.Error()) + return + } + + cpy := p.GenPayload() + + fmt.Printf("Copy and Paste: %s\n", cpy) + + bts, err := p.GenQRCode() + if err != nil { + fmt.Println(err.Error()) + return + } + + fmt.Printf("QRCode: %b\n", bts) ``` ## Roadmap @@ -34,7 +48,7 @@ fmt.Println(cpy) - [x] Static - [x] Dynamic - [ ] Parse and validate EMV Codes -- [ ] Export generated/parsed payment to Image +- [x] Export generated/parsed payment to Image - [x] Export generated/parsed payment to EMV Code - [ ] Fetch, parse and validate remote payloads from dynamic payments - [ ] Verify if has already expired diff --git a/go.mod b/go.mod index 30cc933..75ba885 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,6 @@ go 1.17 require ( github.com/r10r/crc16 v0.1.1 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/snksoft/crc v1.1.0 ) diff --git a/go.sum b/go.sum index fd10459..67f6fa4 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/r10r/crc16 v0.1.1 h1:BsuCL6d+j0hBCQ/nNnwHmlTF0shVES2IagUgh1MS6yU= github.com/r10r/crc16 v0.1.1/go.mod h1:I17p13f8bQGzqw5+futttVdERhbvzpgjlPZdwIJ0ka8= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/snksoft/crc v1.1.0 h1:HkLdI4taFlgGGG1KvsWMpz78PkOC9TkPVpTV/cuWn48= github.com/snksoft/crc v1.1.0/go.mod h1:5/gUOsgAm7OmIhb6WJzw7w5g2zfJi4FrHYgGPdshE+A= diff --git a/main.go b/main.go index 7f5e323..f89122a 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,8 @@ func main() { pix.OptDescription("Teste"), pix.OptMerchantName("Thiago Zilli Sarmento"), pix.OptMerchantCity("Ararangua"), - pix.OptAmount("0.00"), + pix.OptAmount("1.00"), + pix.OptAditionalInfo("gerado por go-pixgen"), pix.OptKind(pix.STATIC), } @@ -21,10 +22,22 @@ func main() { if err != nil { panic(err) } + + if err := p.Validates(); err != nil { + fmt.Println(err.Error()) + return + } + cpy := p.GenPayload() if err != nil { panic(err) } - fmt.Println(cpy) + fmt.Printf("Copy and Paste: %s\n", cpy) + + bts, err := p.GenQRCode() + if err != nil { + panic(err) + } + fmt.Printf("QRCode: %b\n", bts) } diff --git a/pix/const.go b/pix/const.go index 9674494..2d84ebf 100644 --- a/pix/const.go +++ b/pix/const.go @@ -2,7 +2,6 @@ package pix const ( //EmvSchema - //TAG_ONETIME = "1" TAG_INIT = "00" TAG_MAI = "26" TAG_MCC = "52" diff --git a/pix/options.go b/pix/options.go index bda3bb6..8bb9b1e 100644 --- a/pix/options.go +++ b/pix/options.go @@ -14,12 +14,16 @@ func (pix PixKind) String() string { type Options func(o *OptionsParams) error type OptionsParams struct { - id string - pixKey string - description string - amount string - merchant merchant - kind PixKind + id string + pixKey string + description string + amount string + aditionalInfo string + merchant merchant + kind PixKind + url string + qrcodeContent string + qrcodeSize int } type merchant struct { @@ -27,6 +31,27 @@ type merchant struct { city string } +func OptQRCodeSize(value int) Options { + return func(o *OptionsParams) error { + o.qrcodeSize = value + return nil + } +} + +func OptUrl(value string) Options { + return func(o *OptionsParams) error { + o.url = value + return nil + } +} + +func OptAditionalInfo(value string) Options { + return func(o *OptionsParams) error { + o.aditionalInfo = value + return nil + } +} + func OptKind(kind PixKind) Options { return func(o *OptionsParams) error { o.kind = kind @@ -105,3 +130,25 @@ func (o *OptionsParams) GetAmount() string { func (o *OptionsParams) GetKind() PixKind { return o.kind } + +func (o *OptionsParams) GetAditionalInfo() string { + return o.aditionalInfo +} + +func (o *OptionsParams) GetUrl() string { + return o.url +} + +func (o *OptionsParams) GetQRCodeSize() int { + return o.qrcodeSize +} + +func (o *OptionsParams) GetQRCodeContent() string { + return o.qrcodeContent +} + +// ------------- setters + +func (o *OptionsParams) SetQRCodeContent(value string) { + o.qrcodeContent = value +} diff --git a/pix/pix.go b/pix/pix.go index 89b218a..3d48196 100644 --- a/pix/pix.go +++ b/pix/pix.go @@ -1,11 +1,14 @@ package pix import ( + "errors" "fmt" "regexp" "strings" + "unicode/utf8" "github.com/snksoft/crc" + "github.com/thiagozs/go-pixgen/qrcode" ) type Pix struct { @@ -64,6 +67,8 @@ func (p *Pix) GenPayload() string { payload = p.FindAndReplaceCRC(payload) + p.params.SetQRCodeContent(payload) + return payload } @@ -77,15 +82,15 @@ func (p *Pix) generateMAI() string { tags := []string{ p.getValue(TAG_MAI_GUI, BC_GUI), p.getValue(TAG_MAI_PIXKEY, p.params.pixKey), - p.getValue(TAG_MAI_INFO_ADD, "Gerado por Pix-Utils"), - //p.getValue(TAG_MAI_INFO_ADD, p.params.infoAdicional), + } + if len(p.params.aditionalInfo) > 0 { + tags = append(tags, p.getValue(TAG_MAI_INFO_ADD, p.params.aditionalInfo)) } return strings.Join(tags, "") case DYNAMIC: tags := []string{ p.getValue(TAG_MAI_GUI, BC_GUI), - p.getValue(TAG_MAI_URL, "https://www.pix.com.br/"), - //p.getValue(TAG_MAI_URL, p.params.url), + p.getValue(TAG_MAI_URL, p.params.url), } return strings.Join(tags, "") default: @@ -111,3 +116,34 @@ func (p *Pix) FindAndReplaceCRC(payload string) string { payload = m.ReplaceAllString(payload, "") return payload + p.getCRC16(payload) } + +func (p *Pix) Validates() error { + if p.params.pixKey == "" { + return errors.New("pixkey must not be empty") + } + + if p.params.merchant.name == "" { + return errors.New("name must not be empty") + } + + if p.params.merchant.city == "" { + return errors.New("city must not be empty") + } + + if utf8.RuneCountInString(p.params.merchant.name) > 25 { + return errors.New("name must be at least 25 characters long") + } + + if utf8.RuneCountInString(p.params.merchant.city) > 15 { + return errors.New("city must be at least 15 characters long") + } + + return nil +} + +func (p *Pix) GenQRCode() ([]byte, error) { + return qrcode.New(qrcode.QRCodeOptions{ + Size: p.params.GetQRCodeSize(), + Content: p.params.GetQRCodeContent(), + }) +} diff --git a/qrcode/qrcode.go b/qrcode/qrcode.go index 8cabfd3..e32384b 100644 --- a/qrcode/qrcode.go +++ b/qrcode/qrcode.go @@ -1 +1,17 @@ package qrcode + +import ( + "github.com/skip2/go-qrcode" +) + +type QRCodeOptions struct { + Content string + Size int +} + +func New(options QRCodeOptions) ([]byte, error) { + if options.Size == 0 { + options.Size = 256 + } + return qrcode.Encode(options.Content, qrcode.Medium, options.Size) +}