-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e475308
Showing
32 changed files
with
4,199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea/ | ||
*.DS_Store | ||
examples/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
language: go | ||
|
||
go: | ||
- 1.14.x | ||
|
||
before_install: | ||
- go get -t -v ./... | ||
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.24.0 | ||
|
||
script: | ||
- go test ./... -race -coverprofile=coverage.txt -covermode=atomic | ||
- make | ||
|
||
after_success: | ||
- bash <(curl -s https://codecov.io/bash) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
### Required tools | ||
GOTOOLS_CHECK = golangci-lint | ||
|
||
all: fmt ensure-deps linter test | ||
|
||
### Testing | ||
test: | ||
go test ./... -covermode=atomic -coverpkg=./... -count=1 -race | ||
|
||
test-cover: | ||
go test ./... -covermode=atomic -coverprofile=/tmp/coverage.out -coverpkg=./... -count=1 | ||
go tool cover -html=/tmp/coverage.out | ||
|
||
test-integration: | ||
go test -tags integration ./... -covermode=atomic -coverpkg=./... -count=1 -race | ||
|
||
### Formatting, linting, and deps | ||
fmt: | ||
go fmt ./... | ||
|
||
ensure-deps: | ||
@echo "==> Running go mod tidy" | ||
go mod download | ||
go mod tidy | ||
|
||
linter: | ||
@echo "==> Running linter" | ||
golangci-lint run ./... | ||
|
||
# To avoid unintended conflicts with file names, always add to .PHONY | ||
# unless there is a reason not to. | ||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html | ||
.PHONY: check_tools test test-cover fmt linter ensure-deps |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
[![codecov](https://codecov.io/gh/jattento/go-iso8583/branch/master/graph/badge.svg)](https://codecov.io/gh/jattento/go-iso8583) | ||
[![Build Status](https://travis-ci.com/jattento/go-iso8583.svg?branch=master)](https://travis-ci.com/jattento/go-iso8583) | ||
|
||
# go-iso8583 | ||
|
||
An easy to use, yet flexible marshaler for ISO-8583. | ||
|
||
API [godoc](https://godoc.org/github.com/jattento/go-iso8583/pkg/iso8583) documentation. | ||
|
||
This library provides: | ||
- Marshal and Unmarshal functions with his respective interfaces | ||
including MTI, VAR, LLVAR, LLLVAR and bitmaps fields ready for use | ||
but with the possibility to easily add new field types. | ||
- Inbuid Support for ASCII and EBCDIC but not limited to them | ||
|
||
|
||
## Installation | ||
|
||
To install go-iso8583 package, you need to install Go and set your Go workspace first. | ||
|
||
1. First you need [Go](https://golang.org/) installed, then you can use the below Go command to install go-iso8583. | ||
```sh | ||
$ go get -u github.com/jattento/go-iso8583/iso8583 | ||
``` | ||
|
||
2. Import it in your code: | ||
```go | ||
import "github.com/jattento/go-iso8583/pkg/iso8583" | ||
``` | ||
|
||
## Quick start | ||
|
||
```go | ||
import "github.com/go-iso8583/pkg/iso8583" | ||
|
||
type PurchaseRequest struct { | ||
MTI iso8583.MTI `iso8583:"mti"` | ||
FirstBitmap iso8583.BITMAP `iso8583:"bitmap,length:64"` // length is the maximum amount of represented elements. | ||
SecondBitmap iso8583.BITMAP `iso8583:"1,length:64"` // length is the maximum amount of represented elements. | ||
PAN iso8583.LLVAR `iso8583:"2"` | ||
ProcessingCode iso8583.VAR `iso8583:"3"` | ||
Amount iso8583.VAR `iso8583:"4,encoding:ebcdic"` // By default ASCII is assumed but dont limit yourself! | ||
DateTime iso8583.VAR `iso8583:"7"` | ||
SystemTraceAuditNumber iso8583.VAR `iso8583:"11,omitempty"` // omitempty is supported! | ||
LocalTransactionTime iso8583.VAR `iso8583:"12"` | ||
LocalTransactionDate iso8583.VAR `iso8583:"-"` // You can explicitly ignore a field. | ||
ExpirationDate iso8583.VAR `iso8583:"14"` | ||
MerchantType iso8583.VAR `iso8583:"18"` | ||
ICC iso8583.LLLVAR `iso8583:"55"` | ||
SettlementCode iso8583.VAR `iso8583:"66"` | ||
MessageNumber iso8583.VAR `iso8583:"71"` | ||
TransactionDescriptor iso8583.VAR `iso8583:"104"` | ||
} | ||
|
||
func GenerateStaticReqBytes() ([]byte, error) { | ||
req := PurchaseRequest{ | ||
MTI: "0100", | ||
// FirstBitmap is generated by library | ||
// SecondBitmap is generated by library | ||
PAN: "54000000000000111", // LL part is added by library! | ||
ProcessingCode: "1000", | ||
Amount: "000000000100", | ||
MessageNumber: "1", | ||
} | ||
|
||
byt, err := iso8583.Marshal(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return byt, nil | ||
} | ||
``` | ||
|
||
```go | ||
import "github.com/go-iso8583/pkg/iso8583" | ||
|
||
type PurchaseResponse struct { | ||
MTI iso8583.MTI `iso8583:"mti,length:4"` | ||
FirstBitmap iso8583.BITMAP `iso8583:"bitmap,length:64"` // length is the maximum amount of represented elements. | ||
SecondBitmap iso8583.BITMAP `iso8583:"1,length:64"` // length is the maximum amount of represented elements. | ||
PAN iso8583.LLVAR `iso8583:"2,length:2"` // length is the amount of bytes of the LL part. | ||
ProcessingCode iso8583.VAR `iso8583:"3,length:6"` | ||
Amount iso8583.VAR `iso8583:"4,length:12"` | ||
DateTime iso8583.VAR `iso8583:"7,length:10"` | ||
SystemTraceAuditNumber iso8583.VAR `iso8583:"11,length:6"` | ||
LocalTransactionTime iso8583.VAR `iso8583:"12,length:6"` | ||
LocalTransactionDate iso8583.VAR `iso8583:"13,length:4"` | ||
ExpirationDate iso8583.VAR `iso8583:"14,length:4"` | ||
MerchantType iso8583.VAR `iso8583:"18,length:4"` | ||
ResponseCode iso8583.VAR `iso8583:"39,length:2"` | ||
ICC iso8583.LLLVAR `iso8583:"55,length:3,encoding:ebcdic/ascii"` // LLL and VAR part use different encoding? Use a / to indicate both | ||
SettlementCode iso8583.VAR `iso8583:"66,length:1"` | ||
MessageNumber iso8583.VAR `iso8583:"71,length:4"` | ||
TransactionDescriptor iso8583.VAR `iso8583:"104,length:100"` | ||
} | ||
|
||
func ReadResp(byt []byte) (PurchaseResponse,error){ | ||
var resp PurchaseResponse | ||
|
||
_, err :=iso8583.Unmarshal(byt,&resp) | ||
if err != nil{ | ||
return PurchaseResponse{}, err | ||
} | ||
|
||
return resp,nil | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/jattento/go-iso8583 | ||
|
||
go 1.14 | ||
|
||
require github.com/stretchr/testify v1.6.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= | ||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package bitmap | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"math" | ||
) | ||
|
||
type Bitmap = map[int]bool | ||
|
||
const ( | ||
_bitmapLength = 8 | ||
_bitsInByte = 8 | ||
|
||
_firstByteIndex = 0 | ||
_firstBitOffset = 7 | ||
_lastBitOffset = 0 | ||
) | ||
|
||
var ( | ||
// Errors... | ||
ErrBitmapISOWrongLength = errors.New("wrong bitmap length input") | ||
ErrBitmapISOBadBitmapPosition = errors.New("bad bitmap position input") | ||
ErrBitmapISOImpossibleBitmap = errors.New("impossible generate bitmap, lowest and highest limits too far") | ||
ErrBitmapISOFirstBitProhibited = errors.New("first bit can be setted manually in input") | ||
) | ||
|
||
// ISO8583FromBytes indicates which elements of a ISO8583 message are present. | ||
// It receives a 8 byte long ISO8583 bitmap with a position (to indicate if its the first or second). | ||
// Returns a map[int]bool to allow searching by element. | ||
func ISO8583FromBytes(b []byte, bitmapPosition int) (presentElements Bitmap, nextBitmapPresent bool, returnErr error) { | ||
const nextBitmapIndicator = 1 | ||
|
||
// Validate input | ||
if len(b) != _bitmapLength { | ||
return nil, false, | ||
fmt.Errorf("%w: should be %v, but its %v", ErrBitmapISOWrongLength, _bitmapLength, len(b)) | ||
} | ||
|
||
if bitmapPosition < 1 { | ||
return nil, false, | ||
fmt.Errorf("%w: should not be lower than 1, but its %v", ErrBitmapISOBadBitmapPosition, bitmapPosition) | ||
} | ||
|
||
rawBitmap := FromBytes(b) | ||
isoBitmap := make(Bitmap) | ||
|
||
for k, v := range rawBitmap { | ||
// Next bitmap indicator is returned separately in the second return value | ||
if k != nextBitmapIndicator { | ||
isoBitmap[_bitsInByte*_bitmapLength*(bitmapPosition-1)+k] = v | ||
} | ||
} | ||
|
||
return isoBitmap, rawBitmap[nextBitmapIndicator], nil | ||
} | ||
|
||
// ISO8583ToBytes creates a bitmap in byte format. | ||
// Map key 1 must not be present. | ||
func ISO8583ToBytes(b Bitmap, nextBitmapPresent bool) ([]byte, error) { | ||
// Find the highest and lowest element in map | ||
lowestElement, highestElement := Extremities(b) | ||
|
||
inferiorLimit := 1 | ||
for checkedLimit := 1; checkedLimit < lowestElement; checkedLimit += 64 { | ||
inferiorLimit = checkedLimit | ||
} | ||
|
||
superiorLimit := inferiorLimit + 63 | ||
|
||
if superiorLimit < highestElement { | ||
return nil, fmt.Errorf("%w: lowest limit %v (element %v), highest limit %v (element %v)", | ||
ErrBitmapISOImpossibleBitmap, inferiorLimit, lowestElement, superiorLimit, highestElement) | ||
} | ||
|
||
if _, exist := b[inferiorLimit]; exist { | ||
return nil, fmt.Errorf("%w: position %v", ErrBitmapISOFirstBitProhibited, inferiorLimit) | ||
} | ||
|
||
bmap := b | ||
bmap[inferiorLimit] = nextBitmapPresent | ||
|
||
if _, exist := bmap[superiorLimit]; !exist { | ||
bmap[superiorLimit] = false | ||
} | ||
|
||
byt := ToBytes(bmap) | ||
|
||
return byt[len(byt)-_bitmapLength:], nil | ||
} | ||
|
||
// FromBytes given bytes it returns a map[int]bool indicating which biy is on or off. Most left is 1. | ||
func FromBytes(b []byte) Bitmap { | ||
availableElements := make(Bitmap) | ||
|
||
// Iterate over each bit of the input from most left to most right | ||
for bytePosition := _firstByteIndex; bytePosition < len(b); bytePosition++ { | ||
for bitOffset := _firstBitOffset; bitOffset >= _lastBitOffset; bitOffset-- { | ||
// Calculate element position and save in map | ||
previousBytesSummary := _bitsInByte * bytePosition | ||
bitPosition := _bitsInByte - bitOffset | ||
|
||
availableElements[bitPosition+previousBytesSummary] = hasBitSet(b[bytePosition], uint(bitOffset)) | ||
} | ||
} | ||
|
||
return availableElements | ||
} | ||
|
||
// ToBytes creates a bitmap in []byte format. Most left is 1. | ||
func ToBytes(b Bitmap) []byte { | ||
getPosition := func(byt, bit int) int { return bit + byt*_bitsInByte } | ||
|
||
_, highestElement := Extremities(b) | ||
|
||
bmap := make([]byte, int(math.Ceil(float64(highestElement)/_bitsInByte))) | ||
for bytePosition := _firstByteIndex; bytePosition < len(bmap); bytePosition++ { | ||
for bitOffset := _firstBitOffset; bitOffset >= _lastBitOffset; bitOffset-- { | ||
if isOn, exist := b[getPosition(bytePosition, _bitsInByte-bitOffset)]; exist && isOn { | ||
bmap[bytePosition] = setBit(bmap[bytePosition], uint(bitOffset)) | ||
} | ||
} | ||
} | ||
|
||
return bmap | ||
} | ||
|
||
// Returns if the indicated bit is on. | ||
func hasBitSet(n byte, pos uint) bool { | ||
val := n & (1 << pos) | ||
return val > 0 | ||
} | ||
|
||
// Sets the bit at pos in the integer n. | ||
func setBit(n byte, pos uint) byte { | ||
n |= 1 << pos | ||
return n | ||
} | ||
|
||
func Extremities(b Bitmap) (low, high int) { | ||
firstIteration := true | ||
for k := range b { | ||
if k > high { | ||
high = k | ||
} | ||
if k < low || firstIteration { | ||
low = k | ||
} | ||
firstIteration = false | ||
} | ||
|
||
return low, high | ||
} |
Oops, something went wrong.