From fea4f0d3b92d091f58a9bcecc82dba3438d380dc Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 1 Aug 2022 19:42:51 +0100 Subject: [PATCH] restructure --- .../Program.cs | 4 +- .../Properties/launchSettings.json | 3 +- ...ctionProcessing.SettlementProcessor.csproj | 6 +- .../DataGenerator/Program.cs | 9 +- .../Program.cs | 12 +- ...ransactionProcessor.SystemSetupTool.csproj | 4 +- .../appsettings.json | 2 +- .../continuous/CallbackHandlerEnricher.js | 7 +- .../continuous/EstateAggregator.js | 12 +- ...tateManagementSubscriptionStreamBuilder.js | 11 +- .../FileProcessorSubscriptionStreamBuilder.js | 11 +- .../continuous/MerchantAggregator.js | 7 +- .../continuous/MerchantBalanceCalculator.js | 20 +- .../continuous/TransactionEnricher.js | 8 +- ...ctionProcessorSubscriptionStreamBuilder.js | 12 +- .../setupconfig.json | 189 +++++++++++++++++- 16 files changed, 283 insertions(+), 34 deletions(-) diff --git a/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Program.cs b/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Program.cs index aeb9612..a55ccb3 100644 --- a/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Program.cs +++ b/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Program.cs @@ -19,8 +19,8 @@ static async Task Main(string[] args) endDate = DateTime.ParseExact(args[2], "yyyy-MM-dd", null); } - startDate = new DateTime(2021,12,01); - endDate = new DateTime(2022,1,4); + startDate = new DateTime(2022,7,1); + endDate = new DateTime(2022,7,31); SettlementProcessor processor = new SettlementProcessor(); processor.LoadConfiguration(); diff --git a/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Properties/launchSettings.json b/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Properties/launchSettings.json index 6917ae2..6d2ce60 100644 --- a/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Properties/launchSettings.json +++ b/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/Properties/launchSettings.json @@ -2,8 +2,7 @@ "profiles": { "TransactionProcessing.SettlementProcessor": { "commandName": "Project", - //"commandLineArgs": "4FC2692F-067A-443E-8006-335BF2732248 2021-10-27 2021-10-27" - "commandLineArgs": "4FC2692F-067A-443E-8006-335BF2732248" + "commandLineArgs": "435613ac-a468-47a3-ac4f-649d89764c22" } } } \ No newline at end of file diff --git a/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor.csproj b/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor.csproj index def8ec9..a72b752 100644 --- a/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor.csproj +++ b/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor/TransactionProcessing.SettlementProcessor.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net6.0 @@ -10,8 +10,8 @@ - - + + diff --git a/TransactionProcessor.DataGenerator/DataGenerator/Program.cs b/TransactionProcessor.DataGenerator/DataGenerator/Program.cs index 97b80a0..d99296a 100644 --- a/TransactionProcessor.DataGenerator/DataGenerator/Program.cs +++ b/TransactionProcessor.DataGenerator/DataGenerator/Program.cs @@ -99,7 +99,7 @@ static async Task Main(string[] args) Program.TransactionProcessorClient = new TransactionProcessorClient(baseAddressFunc, httpClient); // Set an estate - Guid estateId = Guid.Parse("0f7040a6-e3c1-48ad-9d1b-39c1536fa688"); + Guid estateId = Guid.Parse("435613ac-a468-47a3-ac4f-649d89764c22"); // Get a token await Program.GetToken(CancellationToken.None); @@ -108,8 +108,8 @@ static async Task Main(string[] args) List merchants = await Program.EstateClient.GetMerchants(Program.TokenResponse.AccessToken, estateId, CancellationToken.None); // Set the date range - DateTime startDate = new DateTime(2022,1,5); //27/7 - DateTime endDate = new DateTime(2022,1,5); // This is the date of te last generated transaction + DateTime startDate = new DateTime(2022,8,1); //27/7 + DateTime endDate = new DateTime(2022,8,1); // This is the date of te last generated transaction List dateRange = Program.GenerateDateRange(startDate, endDate); // Only use merchants that have a device @@ -189,10 +189,13 @@ private static async Task GenerateFileUploads(List merchants, await UploadFile(file, merchant.EstateId, merchant.MerchantId, fileProfileId, estateUser.SecurityUserId, fileDateTime, cancellationToken); // Remove file onece uploaded + Console.WriteLine($"File Uploaded for Merchant {merchant.MerchantName}"); File.Delete(file); } } } + + await Task.Delay(10000); } private static Guid GetFileProfileIdFromOperator(String operatorName, CancellationToken cancellationToken) diff --git a/TransactionProcessor.SystemSetupTool/Program.cs b/TransactionProcessor.SystemSetupTool/Program.cs index 805407c..78db8e0 100644 --- a/TransactionProcessor.SystemSetupTool/Program.cs +++ b/TransactionProcessor.SystemSetupTool/Program.cs @@ -85,10 +85,10 @@ private static async Task SetupSubscriptions() foreach (var estate in estateConfiguration.Estates) { - PersistentSubscriptionSettings s = new PersistentSubscriptionSettings(resolveLinkTos: true, maxRetryCount: 5); + PersistentSubscriptionSettings s = new PersistentSubscriptionSettings(resolveLinkTos:true, maxRetryCount:5); // Setup the subscrtipions await PersistentSubscriptionsClient.CreateAsync(estate.Name.Replace(" ", ""), "Reporting", s); - await PersistentSubscriptionsClient.CreateAsync($"FileProcessorSubscriptionStream_{estate.Name.Replace(" ", "")}", "FileProcessor", s); + await PersistentSubscriptionsClient.CreateAsync($"FileProcessorSubscriptionStream_{estate.Name.Replace(" ", "")}", "File sProcessor", s); await PersistentSubscriptionsClient.CreateAsync($"TransactionProcessorSubscriptionStream_{estate.Name.Replace(" ", "")}", "Transaction Processor", s); await Program.PersistentSubscriptionsClient.CreateAsync($"EstateManagementSubscriptionStream_{estate.Name.Replace(" ", "")}", "Estate Management", s); } @@ -105,6 +105,12 @@ private static async Task DeployProjections() FileInfo f = new FileInfo(projection); String name = f.Name.Substring(0, f.Name.Length - (f.Name.Length - f.Name.LastIndexOf("."))); var body = File.ReadAllText(f.FullName); + + var x = body.IndexOf("//endtestsetup"); + x = x + "//endtestsetup".Length; + + body = body.Substring(x); + // Is this already deployed (in the master list) if ( currentProjections.Any(p => p.Name == name) == false) { @@ -212,7 +218,7 @@ private static async Task SetupEstatesFromConfig() foreach (var estate in estateConfiguration.Estates) { - await Program.CreateEstate(estate, CancellationToken.None); + await Program.CreateEstate(estate, CancellationToken.None); } } diff --git a/TransactionProcessor.SystemSetupTool/TransactionProcessor.SystemSetupTool.csproj b/TransactionProcessor.SystemSetupTool/TransactionProcessor.SystemSetupTool.csproj index 427e6f6..a51cd09 100644 --- a/TransactionProcessor.SystemSetupTool/TransactionProcessor.SystemSetupTool.csproj +++ b/TransactionProcessor.SystemSetupTool/TransactionProcessor.SystemSetupTool.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net6.0 @@ -23,7 +23,7 @@ - + diff --git a/TransactionProcessor.SystemSetupTool/appsettings.json b/TransactionProcessor.SystemSetupTool/appsettings.json index 3da65fa..3920db5 100644 --- a/TransactionProcessor.SystemSetupTool/appsettings.json +++ b/TransactionProcessor.SystemSetupTool/appsettings.json @@ -8,6 +8,6 @@ // Staging "EstateManagementUri": "http://192.168.0.133:5000", "SecurityServiceUri": "https://192.168.0.133:5001", - "EventStoreAddress": "esdb://admin:changeit@192.168.0.133:2113?tls=true&tlsVerifyCert=false" + "EventStoreAddress": "esdb://admin:changeit@192.168.0.133:2113?tls=false&tlsVerifyCert=false" } } diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/CallbackHandlerEnricher.js b/TransactionProcessor.SystemSetupTool/projections/continuous/CallbackHandlerEnricher.js index cb2b1fa..30acab3 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/CallbackHandlerEnricher.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/CallbackHandlerEnricher.js @@ -1,3 +1,8 @@ +//starttestsetup +var fromStreams = fromStreams || require('../../node_modules/@transactionprocessing/esprojection-testing-framework').scope.fromStreams; +var emit = emit || require('../../node_modules/@transactionprocessing/esprojection-testing-framework').scope.emit; +//endtestsetup + fromStreams("$ce-EstateAggregate", "$et-CallbackReceivedEvent") .when({ $init: function (s, e) @@ -77,4 +82,4 @@ function getStreamName(estate, e) { return streamName; -} +} \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/EstateAggregator.js b/TransactionProcessor.SystemSetupTool/projections/continuous/EstateAggregator.js index 526357c..3a910e1 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/EstateAggregator.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/EstateAggregator.js @@ -1,3 +1,8 @@ +//starttestsetup +var fromAll = fromAll || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.fromAll; +var linkTo = linkTo || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.linkTo; +//endtestsetup + isEstateEvent = (e) => { return (e.data && e.data.estateId); } isAnEstateCreatedEvent = (e) => { return compareEventTypeSafely(e.eventType, 'EstateCreatedEvent') }; @@ -20,6 +25,8 @@ isTruncated = function (metadata) { return false; }; +getStringWithNoSpaces = function(inputString) { return inputString.replace(/-/gi, "").replace(/ /g, ""); } + fromAll() .when({ $init: function (s, e) { @@ -33,8 +40,7 @@ fromAll() if (isAnEstateCreatedEvent(e)) { s.estates[e.data.estateId] = { - filteredName: e.data.estateName.replace(/-/gi, ""), - name: e.data.estateName.replace(/-/gi, "").replace(" ", "") + name: getStringWithNoSpaces(e.data.estateName) }; } @@ -42,4 +48,4 @@ fromAll() } } } - ); + ); \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/EstateManagementSubscriptionStreamBuilder.js b/TransactionProcessor.SystemSetupTool/projections/continuous/EstateManagementSubscriptionStreamBuilder.js index f6f1ffb..7b69f57 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/EstateManagementSubscriptionStreamBuilder.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/EstateManagementSubscriptionStreamBuilder.js @@ -1,3 +1,8 @@ +//starttestsetup +var fromAll = fromAll || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.fromAll; +var linkTo = linkTo || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.linkTo; +//endtestsetup + isEstateEvent = (e) => { return (e.data && e.data.estateId); } isAnEstateCreatedEvent = (e) => { return compareEventTypeSafely(e.eventType, 'EstateCreatedEvent') }; compareEventTypeSafely = (sourceEventType, targetEventType) => { return (sourceEventType.toUpperCase() === targetEventType.toUpperCase()); } @@ -35,6 +40,8 @@ getStreamName = function (estateName) { return 'EstateManagementSubscriptionStream_' + estateName; } +getStringWithNoSpaces = function (inputString) { return inputString.replace(/-/gi, "").replace(/ /g, ""); } + fromAll() .when({ $init: function (s, e) { @@ -48,7 +55,7 @@ fromAll() if (isAnEstateCreatedEvent(e)) { s.estates[e.data.estateId] = { filteredName: e.data.estateName.replace(/-/gi, ""), - name: e.data.estateName.replace(/-/gi, "").replace(" ", "") + name: getStringWithNoSpaces(e.data.estateName) }; } @@ -58,4 +65,4 @@ fromAll() } } } - ); + ); \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/FileProcessorSubscriptionStreamBuilder.js b/TransactionProcessor.SystemSetupTool/projections/continuous/FileProcessorSubscriptionStreamBuilder.js index a25a997..5cc7d29 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/FileProcessorSubscriptionStreamBuilder.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/FileProcessorSubscriptionStreamBuilder.js @@ -1,3 +1,8 @@ +//starttestsetup +var fromAll = fromAll || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.fromAll; +var linkTo = linkTo || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.linkTo; +//endtestsetup + isEstateEvent = (e) => { return (e.data && e.data.estateId); } isAnEstateCreatedEvent = (e) => { return compareEventTypeSafely(e.eventType, 'EstateCreatedEvent') }; compareEventTypeSafely = (sourceEventType, targetEventType) => { return (sourceEventType.toUpperCase() === targetEventType.toUpperCase()); } @@ -39,6 +44,8 @@ getStreamName = function (estateName) { return 'FileProcessorSubscriptionStream_' + estateName; } +getStringWithNoSpaces = function (inputString) { return inputString.replace(/-/gi, "").replace(/ /g, ""); } + fromAll() .when({ $init: function (s, e) { @@ -52,7 +59,7 @@ fromAll() if (isAnEstateCreatedEvent(e)) { s.estates[e.data.estateId] = { filteredName: e.data.estateName.replace(/-/gi, ""), - name: e.data.estateName.replace(/-/gi, "").replace(" ", "") + name: getStringWithNoSpaces(e.data.estateName) }; } @@ -62,4 +69,4 @@ fromAll() } } } -); +); \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantAggregator.js b/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantAggregator.js index f379c84..8174f30 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantAggregator.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantAggregator.js @@ -1,3 +1,8 @@ +//starttestsetup +var fromAll = fromAll || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.fromAll; +var linkTo = linkTo || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.linkTo; +//endtestsetup + isValidEvent = function (e) { if (e) { @@ -31,4 +36,4 @@ fromAll() } } } - }); + }); \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantBalanceCalculator.js b/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantBalanceCalculator.js index 75bdb93..35a14f1 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantBalanceCalculator.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/MerchantBalanceCalculator.js @@ -1,3 +1,9 @@ +//starttestsetup +var fromCategory = fromCategory || require('../../node_modules/@transactionprocessing/esprojection-testing-framework').scope.fromCategory; +var partitionBy = partitionBy !== null ? partitionBy : require('../../node_modules/@transactionprocessing/esprojection-testing-framework').scope.partitionBy; +var emit = emit || require('../../node_modules/@transactionprocessing/esprojection-testing-framework').scope.emit; +//endtestsetup + fromCategory('MerchantArchive') .foreachStream() .when({ @@ -39,6 +45,11 @@ var eventbus = { return; } + if (e.eventType === 'AutomaticDepositMadeEvent') { + depositMadeEventHandler(s, e); + return; + } + if (e.eventType === 'TransactionHasStartedEvent') { transactionHasStartedEventHandler(s, e); return; @@ -126,7 +137,7 @@ var merchantCreatedEventHandler = function (s, e) { var emitBalanceChangedEvent = function (aggregateId, eventId, s, changeAmount, dateTime, reference) { if (s.initialised === true) { - + // Emit an opening balance event var openingBalanceEvent = { $type: getEventTypeName(), @@ -175,8 +186,7 @@ var depositMadeEventHandler = function (s, e) { incrementBalanceFromDeposit(s, e.data.amount, e.data.depositDateTime); // emit an balance changed event here - console.log(e); - s = emitBalanceChangedEvent(e.data.merchantId, e.eventId, s, e.data.amount, e.data.depositDateTime, "Merchant Deposit"); + emitBalanceChangedEvent(e.data.merchantId, e.eventId, s, e.data.amount, e.data.depositDateTime, "Merchant Deposit"); }; var transactionHasStartedEventHandler = function (s, e) { @@ -239,7 +249,7 @@ var merchantFeeAddedToTransactionEventHandler = function (s, e) { // increment the balance now incrementBalanceFromMerchantFee(s, e.data.calculatedValue, e.data.feeCalculatedDateTime); - + // emit an balance changed event here s = emitBalanceChangedEvent(e.data.transactionId, e.eventId, s, e.data.calculatedValue, e.data.feeCalculatedDateTime, "Transaction Fee Processed"); -} +} \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionEnricher.js b/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionEnricher.js index bfb9727..271dead 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionEnricher.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionEnricher.js @@ -1,3 +1,9 @@ +//starttestsetup +var fromCategory = fromCategory || require('../../node_modules/@transactionprocessing/esprojection-testing-framework').scope.fromCategory; +var emit = emit || require('../../node_modules/@transactionprocessing/esprojection-testing-framework').scope.emit; +var linkTo = linkTo || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.linkTo; +//endtestsetup + fromCategory('TransactionAggregate') .foreachStream() .when({ @@ -61,4 +67,4 @@ function serviceProviderFeeAddedToTransactionEventHandler(s, e) { function getStreamName(s) { return "TransactionEnricherResult"; -} +} \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionProcessorSubscriptionStreamBuilder.js b/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionProcessorSubscriptionStreamBuilder.js index bb2b3b9..3a0b08b 100644 --- a/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionProcessorSubscriptionStreamBuilder.js +++ b/TransactionProcessor.SystemSetupTool/projections/continuous/TransactionProcessorSubscriptionStreamBuilder.js @@ -1,3 +1,8 @@ +//starttestsetup +var fromAll = fromAll || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.fromAll; +var linkTo = linkTo || require("../../node_modules/@transactionprocessing/esprojection-testing-framework").scope.linkTo; +//endtestsetup + isEstateEvent = (e) => { return (e.data && e.data.estateId); } isAnEstateCreatedEvent = (e) => { return compareEventTypeSafely(e.eventType, 'EstateCreatedEvent') }; compareEventTypeSafely = (sourceEventType, targetEventType) => { return (sourceEventType.toUpperCase() === targetEventType.toUpperCase()); } @@ -8,6 +13,7 @@ getSupportedEventTypes = function () { eventTypes.push('CustomerEmailReceiptRequestedEvent'); eventTypes.push('TransactionHasBeenCompletedEvent'); + eventTypes.push('MerchantFeeAddedToTransactionEvent'); return eventTypes; } @@ -33,6 +39,8 @@ getStreamName = function (estateName) { return 'TransactionProcessorSubscriptionStream_' + estateName; } +getStringWithNoSpaces = function (inputString) { return inputString.replace(/-/gi, "").replace(/ /g, ""); } + fromAll() .when({ $init: function (s, e) { @@ -46,7 +54,7 @@ fromAll() if (isAnEstateCreatedEvent(e)) { s.estates[e.data.estateId] = { filteredName: e.data.estateName.replace(/-/gi, ""), - name: e.data.estateName.replace(/-/gi, "").replace(" ", "") + name: getStringWithNoSpaces(e.data.estateName) }; } @@ -56,4 +64,4 @@ fromAll() } } } -); +); \ No newline at end of file diff --git a/TransactionProcessor.SystemSetupTool/setupconfig.json b/TransactionProcessor.SystemSetupTool/setupconfig.json index 5060dc7..234f2be 100644 --- a/TransactionProcessor.SystemSetupTool/setupconfig.json +++ b/TransactionProcessor.SystemSetupTool/setupconfig.json @@ -1,7 +1,7 @@ { "estates": [ { - "id": "", + "id": "435613AC-A468-47A3-AC4F-649D89764C22", "name": "Demo Estate", "email_address": "estateuser@demoestate1.co.uk", "user": { @@ -268,6 +268,193 @@ ] } ] + }, + + { + "id": "7C757C2C-4EC9-4D78-AC9B-3A7BFC6D5877", + "name": "Demo Estate 2", + "email_address": "estateuser@demoestate2.co.uk", + "user": { + "email_address": "estateuser@demoestate2.co.uk", + "password": "123456", + "given_name": "Demo Estate", + "middle_name": "", + "family_name": "User" + }, + "merchants": [ + { + "name": "Test Merchant 4", + "settlementschedule": "Immediate", + "address": { + "address_line_1": "test address line 1", + "country": "United Kingdom", + "region": "Region", + "town": "MyTown" + }, + "contact": { + "contact_name": "Test Contact", + "email_address": "stuart_ferguson1@outlook.com" + }, + "user": { + "email_address": "merchantuser@testmerchant4.co.uk", + "password": "123456", + "given_name": "Test Merchant 4", + "middle_name": "", + "family_name": "User" + }, + "device": { + "device_identifier": "testmerchant4device" + } + }, + { + "name": "Test Merchant 5", + "settlementschedule": "Weekly", + "address": { + "address_line_1": "test address line 1", + "country": "United Kingdom", + "region": "Region", + "town": "MyTown" + }, + "contact": { + "contact_name": "Test Contact", + "email_address": "stuart_ferguson1@outlook.com" + }, + "operators": [ + "Safaricom", + "Voucher" + ], + "user": { + "email_address": "merchantuser@testmerchant5.co.uk", + "password": "123456", + "given_name": "Test Merchant 5", + "middle_name": "", + "family_name": "User" + }, + "device": { + "device_identifier": "testmerchant5device" + } + }, + { + "name": "Test Merchant 6", + "settlementschedule": "Monthly", + "address": { + "address_line_1": "test address line 1", + "country": "United Kingdom", + "region": "Region", + "town": "MyTown" + }, + "contact": { + "contact_name": "Test Contact", + "email_address": "stuart_ferguson1@outlook.com" + }, + "operators": [ + "Safaricom", + "Voucher" + ], + "user": { + "email_address": "merchantuser@testmerchant6.co.uk", + "password": "123456", + "given_name": "Test Merchant 6", + "middle_name": "", + "family_name": "User" + }, + "device": { + "device_identifier": "testmerchant6device" + } + } + ], + "operators": [ + { + "name": "Safaricom", + "require_custom_merchant_number": false, + "require_custom_terminal_number": false + }, + { + "name": "Voucher", + "require_custom_merchant_number": false, + "require_custom_terminal_number": false + } + ], + "contracts": [ + { + "operator_name": "Safaricom", + "description": "Safaricom Contract", + "products": [ + { + "display_text": "150 KES", + "product_name": "150 KES Topup", + "value": 150.00, + "transaction_fees": [ + { + "calculation_type": 0, + "description": "Merchant Commission", + "value": 0.25, + "fee_type": 0 + } + ] + }, + { + "display_text": "250 KES", + "product_name": "250 KES Topup", + "value": 250.00, + "transaction_fees": [ + { + "calculation_type": 0, + "description": "Merchant Commission", + "value": 0.75, + "fee_type": 0 + } + ] + }, + { + "display_text": "Custom", + "product_name": "Custom", + "value": null, + "transaction_fees": [ + { + "calculation_type": 0, + "description": "Merchant Commission", + "value": 0.35, + "fee_type": 0 + } + ] + } + ] + }, + { + "operator_name": "Voucher", + "description": "Healthcare Centre 1 Contract", + "products": [ + { + "display_text": "50 KES", + "product_name": "50 KES Voucher", + "value": 50.00, + "transaction_fees": [ + { + "calculation_type": 0, + "description": "Merchant Commission", + "value": 0.45, + "fee_type": 0 + } + ] + }, + { + "display_text": "Custom", + "product_name": "Custom", + "value": null, + "transaction_fees": [ + { + "calculation_type": 0, + "description": "Merchant Commission", + "value": 0.55, + "fee_type": 0 + } + ] + } + ] + } + ] } + ] } \ No newline at end of file