Skip to content

Commit

Permalink
refactor(qris): add terminal label parameter and improve additional i…
Browse files Browse the repository at this point in the history
…nformation
  • Loading branch information
fyvri committed Dec 23, 2024
1 parent 98c3c59 commit c07d6a9
Show file tree
Hide file tree
Showing 34 changed files with 2,454 additions and 861 deletions.
6 changes: 3 additions & 3 deletions api/handlers/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var (

type mockQRISController struct {
ParseFunc func(qrisString string) (*entities.QRIS, error, *[]string)
ConvertFunc func(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) (string, string, error, *[]string)
ConvertFunc func(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) (string, string, error, *[]string)
IsValidFunc func(qrisString string) (error, *[]string)
}

Expand All @@ -27,9 +27,9 @@ func (m *mockQRISController) Parse(qrisString string) (*entities.QRIS, error, *[
return nil, nil, nil
}

func (m *mockQRISController) Convert(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) (string, string, error, *[]string) {
func (m *mockQRISController) Convert(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) (string, string, error, *[]string) {
if m.ConvertFunc != nil {
return m.ConvertFunc(qrisString, merchantCityValue, merchantPostalCodeValue, paymentAmountValue, paymentFeeCategoryValue, paymentFeeValue)
return m.ConvertFunc(qrisString, merchantCityValue, merchantPostalCodeValue, paymentAmountValue, paymentFeeCategoryValue, paymentFeeValue, terminalLabelValue)
}
return "", "", nil, nil
}
Expand Down
13 changes: 9 additions & 4 deletions api/handlers/qris.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@ type ParseRequest struct {
QRString string `json:"qr_string"`
}

type ConverterRequest struct {
type IsValidRequest struct {
QRString string `json:"qr_string"`
}

type ConvertRequest struct {
QRString string `json:"qr_string"`
MerchantCity string `json:"merchant_city"`
MerchantPostalCode string `json:"merchant_postal_code"`
PaymentAmount uint32 `json:"payment_amount"`
PaymentFeeCategory string `json:"payment_fee_category"`
PaymentFee uint32 `json:"payment_fee"`
TerminalLabel string `json:"terminal_label"`
}

func NewQRIS(qrisController controllers.QRISInterface) QRISInterface {
Expand Down Expand Up @@ -70,7 +75,7 @@ func (h *QRIS) Parse(c *gin.Context) {
}

func (h *QRIS) Convert(c *gin.Context) {
var req ConverterRequest
var req ConvertRequest

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, Response{
Expand All @@ -82,7 +87,7 @@ func (h *QRIS) Convert(c *gin.Context) {
return
}

qrString, qrCode, err, errs := h.qrisController.Convert(req.QRString, req.MerchantCity, req.MerchantPostalCode, req.PaymentAmount, req.PaymentFeeCategory, req.PaymentFee)
qrString, qrCode, err, errs := h.qrisController.Convert(req.QRString, req.MerchantCity, req.MerchantPostalCode, req.PaymentAmount, req.PaymentFeeCategory, req.PaymentFee, req.TerminalLabel)
if err != nil {
c.JSON(http.StatusInternalServerError, Response{
Success: false,
Expand All @@ -108,7 +113,7 @@ func (h *QRIS) Convert(c *gin.Context) {
}

func (h *QRIS) IsValid(c *gin.Context) {
var req ParseRequest
var req IsValidRequest

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, Response{
Expand Down
4 changes: 2 additions & 2 deletions api/handlers/qris_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func TestQRISConvert(t *testing.T) {
name: "Error: h.qrisController.Convert()",
fields: QRIS{
qrisController: &mockQRISController{
ConvertFunc: func(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) (string, string, error, *[]string) {
ConvertFunc: func(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) (string, string, error, *[]string) {
return "", "", fmt.Errorf("invalid QR string"), nil
},
},
Expand All @@ -188,7 +188,7 @@ func TestQRISConvert(t *testing.T) {
name: "Success",
fields: QRIS{
qrisController: &mockQRISController{
ConvertFunc: func(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) (string, string, error, *[]string) {
ConvertFunc: func(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) (string, string, error, *[]string) {
return "QR Dynamic String", "QR Dynamic Code", nil, nil
},
},
Expand Down
82 changes: 59 additions & 23 deletions api/routes/qris.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ import (

func NewQRISRouter(env *bootstrap.Env, group *gin.RouterGroup) {
qrisTags := &usecases.QRISTags{
VersionTag: config.VersionTag,
CategoryTag: config.CategoryTag,
AcquirerTag: config.AcquirerTag,
AcquirerBankTransferTag: config.AcquirerBankTransferTag,
SwitchingTag: config.SwitchingTag,
MerchantCategoryCodeTag: config.MerchantCategoryCodeTag,
CurrencyCodeTag: config.CurrencyCodeTag,
PaymentAmountTag: config.PaymentAmountTag,
PaymentFeeCategoryTag: config.PaymentFeeCategoryTag,
PaymentFeeFixedTag: config.PaymentFeeFixedTag,
PaymentFeePercentTag: config.PaymentFeePercentTag,
CountryCodeTag: config.CountryCodeTag,
MerchantNameTag: config.MerchantNameTag,
MerchantCityTag: config.MerchantCityTag,
MerchantPostalCodeTag: config.MerchantPostalCodeTag,
AdditionalInformationTag: config.AdditionalInformationTag,
CRCCodeTag: config.CRCCodeTag,
Version: config.VersionTag,
Category: config.CategoryTag,
Acquirer: config.AcquirerTag,
AcquirerBankTransfer: config.AcquirerBankTransferTag,
Switching: config.SwitchingTag,
MerchantCategoryCode: config.MerchantCategoryCodeTag,
CurrencyCode: config.CurrencyCodeTag,
PaymentAmount: config.PaymentAmountTag,
PaymentFeeCategory: config.PaymentFeeCategoryTag,
PaymentFeeFixed: config.PaymentFeeFixedTag,
PaymentFeePercent: config.PaymentFeePercentTag,
CountryCode: config.CountryCodeTag,
MerchantName: config.MerchantNameTag,
MerchantCity: config.MerchantCityTag,
MerchantPostalCode: config.MerchantPostalCodeTag,
AdditionalInformation: config.AdditionalInformationTag,
CRCCode: config.CRCCodeTag,
}
qrisCategoryContents := &usecases.QRISCategoryContents{
Static: config.CategoryStaticContent,
Expand All @@ -39,16 +39,52 @@ func NewQRISRouter(env *bootstrap.Env, group *gin.RouterGroup) {
Fixed: config.PaymentFeeCategoryFixedContent,
Percent: config.PaymentFeeCategoryPercentContent,
}
acquirerDetailTags := &usecases.AcquirerDetailTags{
Site: config.AcquirerDetailSiteTag,
MPAN: config.AcquirerDetailMPANTag,
TerminalID: config.AcquirerDetailTerminalIDTag,
Category: config.AcquirerDetailCategoryTag,
}
switchingDetailTags := &usecases.SwitchingDetailTags{
Site: config.SwitchingDetailSiteTag,
NMID: config.SwitchingDetailNMIDTag,
Category: config.SwitchingDetailCategoryTag,
}
qrisAdditionalInformationDetailTags := &usecases.AdditionalInformationDetailTags{
BillNumber: config.AdditionalInformationDetailBillNumberTag,
MobileNumber: config.AdditionalInformationDetailMobileNumberTag,
StoreLabel: config.AdditionalInformationDetailStoreLabelTag,
LoyaltyNumber: config.AdditionalInformationDetailLoyaltyNumberTag,
ReferenceLabel: config.AdditionalInformationDetailReferenceLabelTag,
CustomerLabel: config.AdditionalInformationDetailCustomerLabelTag,
TerminalLabel: config.AdditionalInformationDetailTerminalLabelTag,
PurposeOfTransaction: config.AdditionalInformationDetailPurposeOfTransactionTag,
AdditionalConsumerDataRequest: config.AdditionalInformationDetailAdditionalConsumerDataRequestTag,
MerchantTaxID: config.AdditionalInformationDetailMerchantTaxIDTag,
MerchantChannel: config.AdditionalInformationDetailMerchantChannelTag,
RFUStart: config.AdditionalInformationDetailRFUTagStart,
RFUEnd: config.AdditionalInformationDetailRFUTagEnd,
PaymentSystemSpecificStart: config.AdditionalInformationDetailPaymentSystemSpecificTagStart,
PaymentSystemSpecificEnd: config.AdditionalInformationDetailPaymentSystemSpecificTagEnd,
}

dataUsecase := usecases.NewData()
acquirerUsecase := usecases.NewAcquirer(dataUsecase, config.AcquirerDetailSiteTag, config.AcquirerDetailMPANTag, config.AcquirerDetailTerminalIDTag, config.AcquirerDetailCategoryTag)
switchingUsecase := usecases.NewSwitching(dataUsecase, config.SwitchingDetailSiteTag, config.SwitchingDetailNMIDTag, config.SwitchingDetailCategoryTag)
fieldUsecase := usecases.NewField(acquirerUsecase, switchingUsecase, qrisTags, qrisCategoryContents)
acquirerUsecase := usecases.NewAcquirer(dataUsecase, acquirerDetailTags)
switchingUsecase := usecases.NewSwitching(dataUsecase, switchingDetailTags)
additionalInformationUsecase := usecases.NewAdditionalInformation(dataUsecase, qrisAdditionalInformationDetailTags)
paymentFeeUsecase := usecases.NewPaymentFee(qrisTags, qrisPaymentFeeCategoryContents)
fieldUsecase := usecases.NewField(acquirerUsecase, switchingUsecase, additionalInformationUsecase, qrisTags, qrisCategoryContents)
crc16CCITTUsecase := usecases.NewCRC16CCITT()

qrisUsecases := &usecases.QRISUsecases{
Data: dataUsecase,
Field: fieldUsecase,
PaymentFee: paymentFeeUsecase,
AdditionalInformation: additionalInformationUsecase,
CRC16CCITT: crc16CCITTUsecase,
}
qrisUsecase := usecases.NewQRIS(
dataUsecase,
fieldUsecase,
crc16CCITTUsecase,
qrisUsecases,
qrisTags,
qrisCategoryContents,
qrisPaymentFeeCategoryContents,
Expand Down
1 change: 1 addition & 0 deletions api/routes/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func Setup(env *bootstrap.Env, ginEngine *gin.Engine) {
"payment_amount": 1337,
"payment_fee_category": "FIXED",
"payment_fee": 666,
"terminal_label": "Made with love by Alvriyanto Azis",
},
},
map[string]any{
Expand Down
19 changes: 19 additions & 0 deletions internal/config/additional_information_tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package config

var (
AdditionalInformationDetailBillNumberTag = "01"
AdditionalInformationDetailMobileNumberTag = "02"
AdditionalInformationDetailStoreLabelTag = "03"
AdditionalInformationDetailLoyaltyNumberTag = "04"
AdditionalInformationDetailReferenceLabelTag = "05"
AdditionalInformationDetailCustomerLabelTag = "06"
AdditionalInformationDetailTerminalLabelTag = "07"
AdditionalInformationDetailPurposeOfTransactionTag = "08"
AdditionalInformationDetailAdditionalConsumerDataRequestTag = "09"
AdditionalInformationDetailMerchantTaxIDTag = "10"
AdditionalInformationDetailMerchantChannelTag = "11"
AdditionalInformationDetailRFUTagStart = "12"
AdditionalInformationDetailRFUTagEnd = "49"
AdditionalInformationDetailPaymentSystemSpecificTagStart = "50"
AdditionalInformationDetailPaymentSystemSpecificTagEnd = "99"
)
24 changes: 24 additions & 0 deletions internal/domain/entities/additional_information.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package entities

type AdditionalInformation struct {
Tag string `json:"tag"`
Content string `json:"content"`
Data string `json:"data"`
Detail AdditionalInformationDetail `json:"detail"`
}

type AdditionalInformationDetail struct {
BillNumber Data `json:"bill_number"`
MobileNumber Data `json:"mobile_number"`
StoreLabel Data `json:"store_label"`
LoyaltyNumber Data `json:"loyalty_number"`
ReferenceLabel Data `json:"reference_label"`
CustomerLabel Data `json:"customer_label"`
TerminalLabel Data `json:"terminal_label"`
PurposeOfTransaction Data `json:"purpose_of_transaction"`
AdditionalConsumerDataRequest Data `json:"additional_consumer_data_request"`
MerchantTaxID Data `json:"merchant_tax_id"`
MerchantChannel Data `json:"merchant_channel"`
RFU Data `json:"rfu"`
PaymentSystemSpecific Data `json:"payment_system_specific"`
}
30 changes: 15 additions & 15 deletions internal/domain/entities/qris.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package entities

type QRIS struct {
Version Data `json:"version"`
Category Data `json:"category"`
Acquirer Acquirer `json:"acquirer"`
Switching Switching `json:"switching"`
MerchantCategoryCode Data `json:"merchant_category_code"`
CurrencyCode Data `json:"currency_code"`
PaymentAmount Data `json:"payment_amount"`
PaymentFeeCategory Data `json:"payment_fee_category"`
PaymentFee Data `json:"payment_fee"`
CountryCode Data `json:"country_code"`
MerchantName Data `json:"merchant_name"`
MerchantCity Data `json:"merchant_city"`
MerchantPostalCode Data `json:"merchant_postal_code"`
AdditionalInformation Data `json:"additional_information"`
CRCCode Data `json:"crc_code"`
Version Data `json:"version"`
Category Data `json:"category"`
Acquirer Acquirer `json:"acquirer"`
Switching Switching `json:"switching"`
MerchantCategoryCode Data `json:"merchant_category_code"`
CurrencyCode Data `json:"currency_code"`
PaymentAmount Data `json:"payment_amount"`
PaymentFeeCategory Data `json:"payment_fee_category"`
PaymentFee Data `json:"payment_fee"`
CountryCode Data `json:"country_code"`
MerchantName Data `json:"merchant_name"`
MerchantCity Data `json:"merchant_city"`
MerchantPostalCode Data `json:"merchant_postal_code"`
AdditionalInformation AdditionalInformation `json:"additional_information"`
CRCCode Data `json:"crc_code"`
}
6 changes: 3 additions & 3 deletions internal/interface/controllers/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var (
type mockQRISUsecase struct {
ParseFunc func(qrString string) (*entities.QRIS, error, *[]string)
IsValidFunc func(qris *entities.QRIS) bool
ModifyFunc func(qris *entities.QRIS, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) *entities.QRIS
ModifyFunc func(qris *entities.QRIS, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) *entities.QRIS
ToStringFunc func(qris *entities.QRIS) string
}

Expand All @@ -39,9 +39,9 @@ func (m *mockQRISUsecase) IsValid(qris *entities.QRIS) bool {
return false
}

func (m *mockQRISUsecase) Modify(qris *entities.QRIS, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) *entities.QRIS {
func (m *mockQRISUsecase) Modify(qris *entities.QRIS, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) *entities.QRIS {
if m.ModifyFunc != nil {
return m.ModifyFunc(qris, merchantCityValue, merchantPostalCodeValue, paymentAmountValue, paymentFeeCategoryValue, paymentFeeValue)
return m.ModifyFunc(qris, merchantCityValue, merchantPostalCodeValue, paymentAmountValue, paymentFeeCategoryValue, paymentFeeValue, terminalLabelValue)
}
return nil
}
Expand Down
25 changes: 20 additions & 5 deletions internal/interface/controllers/qris.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type QRIS struct {

type QRISInterface interface {
Parse(qrisString string) (*entities.QRIS, error, *[]string)
Convert(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) (string, string, error, *[]string)
Convert(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) (string, string, error, *[]string)
IsValid(qrisString string) (error, *[]string)
}

Expand All @@ -37,17 +37,32 @@ func (c *QRIS) Parse(qrisString string) (*entities.QRIS, error, *[]string) {
return c.qrisUsecase.Parse(qrisString)
}

func (c *QRIS) Convert(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32) (string, string, error, *[]string) {
func (c *QRIS) Convert(qrisString string, merchantCityValue string, merchantPostalCodeValue string, paymentAmountValue uint32, paymentFeeCategoryValue string, paymentFeeValue uint32, terminalLabelValue string) (string, string, error, *[]string) {
errs := &[]string{}
merchantCityValue = c.inputUtil.Sanitize(merchantCityValue)
if len(merchantCityValue) > 15 {
*errs = append(*errs, "merchant city exceeds 15 characters")
}
merchantPostalCodeValue = c.inputUtil.Sanitize(merchantPostalCodeValue)
if len(merchantPostalCodeValue) > 10 {
*errs = append(*errs, "merchant postal code exceeds 10 characters")
}
terminalLabelValue = c.inputUtil.Sanitize(terminalLabelValue)
if len(terminalLabelValue) > 99 {
*errs = append(*errs, "terminal label exceeds 99 characters")
}
if len(*errs) > 0 {
return "", "", fmt.Errorf("input length exceeds the maximum permitted characters"), errs
}

qrisString = c.inputUtil.Sanitize(qrisString)
qris, err, errs := c.qrisUsecase.Parse(qrisString)
if err != nil {
return "", "", err, errs
}

merchantCityValue = c.inputUtil.Sanitize(merchantCityValue)
merchantPostalCodeValue = c.inputUtil.Sanitize(merchantPostalCodeValue)
paymentFeeCategoryValue = strings.ToUpper(c.inputUtil.Sanitize(paymentFeeCategoryValue))
qris = c.qrisUsecase.Modify(qris, merchantCityValue, merchantPostalCodeValue, paymentAmountValue, paymentFeeCategoryValue, paymentFeeValue)
qris = c.qrisUsecase.Modify(qris, merchantCityValue, merchantPostalCodeValue, paymentAmountValue, paymentFeeCategoryValue, paymentFeeValue, terminalLabelValue)
qrisString = c.qrisUsecase.ToString(qris)

qrCode, err := c.qrCodeUtil.StringToImageBase64(qrisString, c.qrCodeSize)
Expand Down
Loading

0 comments on commit c07d6a9

Please sign in to comment.