Ask a Dev
Custom Money Out Integration for ACH
Use Cases
- User enables new ACH money out option
- Cloudspark: New CloudSpark page with a form that does the work?
- Disco: Impersonate into CS, create a link into the web office page, or create a custom Disco page.
- User updates ACH money out option
- User fills out the same page as in use case 1. Data is replaced.
- User wants to know where their payment is
- User sees data on the page (as in use case 1)
Solution
Software Structure
Cambridge application needs to be developed in a NuGet package owned, maintained, and deployed by Cambridge. The Client Extension will reference this package during installation.
The solution must be developed in the DirectScale Client Extension.
Cloudspark elements will need to be added through the Admin tool. The outcome of the project will be clear documentation surrounding the setup and management of admin elements.
Task 1 - Build Disco API to Set Cambridge Data on Disco
Custom API build in Disco
- Set the associate to the Cambridge account
- Set the unique Beneficiary ID on that account
Task 2 - Allow User to Enable and Manage ACH Money-out Option
Option 1
Preferred: Create a form that validates the fields and calls custom API (Task 1) to set that associate to Cambridge and adds their beneficiary ID to the database. This form would be owned, hosted, and controlled by Cambridge. (Cambridge not ready for this option right now)
- DS enables SSO on the links to the page.
- Cambridge develops DS SSO link
- Dev team adds a custom page in Web Office that iFrames Cambridge page.
- Call Custom API
Option 2
Create a custom page in the web office that validates the IBan and does the bank search calling out to Cambridge and calls an API in disco (Task 1) to set the Unique Beneficiary ID for that user.
Task 3 - Implement the Money-out Interaction with Cambridge
Custom Money Out Merchant
Create a custom class inside that inherits from AccountCommissionMerchant and complete the different methods
PayCommissions - This is the location that will tell Cambridge how much money each associate has earned and will return the status of those payments.
ProvisionAccount - could inform the associate to go to the custom page and create their account
public class CustomHyperWalletMoneyOutMerchant : AccountCommissionMerchant {
private const string ACCOUNT_NUMBER_KEY = "ActNum";
public CustomHyperWalletMoneyOutMerchant()
: base( new MerchantInfo(5555, "CustomHyperWallet", "USD")
, new List<CommissionMerchantCustomField>
{
new CommissionMerchantCustomField
{
DisplayText = "Account Number",
Key = ACCOUNT_NUMBER_KEY,
IsRequired = true
}
})
{
}
public override CommissionPaymentResults PayCommissions(int batchId, List<CommissionPayment> payments)
{
var results = new List<CommissionPaymentResult>();
foreach (var payment in payments)
{
// Be careful here!
// We process the whole batch at once, so you have to provide the error handling so the statuses are correctly saved.
// If half of the payments succeeded, but then an exception were thrown, disco would not receive the CommissionPaymentResults
// and would be unaware that any of the payments went out. Try - catch handling is necessary
try
{
if (payment.MerchantCustomFields.TryGetValue(ACCOUNT_NUMBER_KEY, out string accountNumber))
{
// call out to merchant to process payment
results.Add(new CommissionPaymentResult()
{
PaymentUniqueId = payment.PaymentUniqueId, // This one is important that you manually set this equal to the payment you just processed
TransactionNumber = payment.PaymentUniqueId,
Status = CommissionPaymentStatus.Paid,
DatePaid = DateTime.Now,
CheckNumber = 100,
});
}
else
{
results.Add(new CommissionPaymentResult()
{
PaymentUniqueId = payment.PaymentUniqueId, // This one is important that you manually set this equal to the payment you just processed
Status = CommissionPaymentStatus.Failed,
ErrorMessage = "Account not initialized"
});
}
}
catch (Exception e)
{
results.Add(new CommissionPaymentResult()
{
PaymentUniqueId = payment.PaymentUniqueId, // This one is important that you manually set this equal to the payment you just processed
Status = CommissionPaymentStatus.Failed,
ErrorMessage = $"Exception ocurred while paying {payment.AssociateId} : {e.Message}"
}) ;
}
}
return new CommissionPaymentResults
{
Results = results
};
}
public override CommissionMerchantAccountFields ProvisionAccount(int associateID)
{
// Create an account for this associate, and any other custom values you need in PayCommissions
return new CommissionMerchantAccountFields
{
CustomValues = new Dictionary<string, string>
{
{ ACCOUNT_NUMBER_KEY, "Response account number" }
}
};
}
}
}
Read more: Setting Up a Money Out Payment Provider
Task 4 - Credentials
We discussed the Partner Key (DirectScale) being stored in the Nu Get package since it will not change. The client key and (another one) will need to be either passed in the constructor from the registration in the Client DLL OR stored in a DB table and retrieved. This may include CRUD for management of the creds.