Domestic Wire
Domestic wire adapters facilitate wire transfers between accounts at U.S. financial institutions and allow end users to send funds within the U.S. by connecting to the relevant wire network, such as the Federal Reserve Wire Network (Fedwire). Building out an SDK extension allows you to hook into this flow, allowing your extension to act as an intermediary to a wire transfer provider and providing you with the ability and flexibility to customize and perform wire transfers.
With the Domestic Wire Adapter, users can:
Initiate one-time wire transfers to a recipient at any domestic financial institution.
Schedule future-date wire transfers for specific payment dates or set up recurring wire transfer series for regular payments.
By default, the following boilerplate code will be generated in your extension:
async def handle_domestic_wire(
self, domestic_wire: DomesticWireRequest, source_account: HostAccountRequest
) -> bool:
domestic_wire
: Contains the destination and transaction details submitted by the end user.source_account
: Contains the details of the account from which the transfer is initiated.
This method is expected to return a boolean value, which indicates whether the wire transfer was successful. You can customize the logic within this function to manipulate the flow of the domestic wire transfer, add validations, and interact with external systems.
Walkthrough
Now, let’s walk through an example implementation for handling wire transfers.
We’ll explain the key steps involved in the logic, starting with the handle_domestic_wire
method and diving into the helper functions used to
prepare the transfer and communicate with the external wire service.
Import Required Libraries
To begin, you’ll need to import the relevant libraries for the operation:
import json from q2_sdk.core import q2_requests from datetime import datetime
Perform the wire transfer
The
handle_domestic_wire
method is the entry point for processing a domestic wire transfer. It takes in two key parameters:domestic_wire
andsource_account
, which contain the wire details and the account information, respectively:async def handle_domestic_wire( self, domestic_wire: DomesticWireRequest, source_account: HostAccountRequest ) -> bool: """ Perform your domestic wire transfer here. If you return true, a transaction will be generated and visible throughout the q2 platform-- only return 'true' on a success. :param domestic_wire: Wire destination and details submitted by user :param source_account: The account from which to source the transfer """ payload = serialize_payload_to_json(domestic_wire, source_account) response = await self.send_wire_transfer(payload) if not response.get("success"): return False return True
Explanation:
The
handle_domestic_wire
method is responsible for orchestrating the wire transfer.It calls the
serialize_payload_to_json
function to convert the wire details and source account data into the required JSON payload.Next, it sends the payload to an external service using the
send_wire_transfer
method.It then checks if the response indicates a successful transaction and logs the result.
Important Details:
The method returns
True
on a successful transfer andFalse
on failure.Logs are used to provide detailed information on the status of the wire transfer.
The
serialize_payload_to_json
method converts the wire details and the source account information into a JSON-compatible format, which is then sent to the wire provider.def serialize_payload_to_json( domestic_wire: DomesticWireRequest, source_account: HostAccountRequest ): current_date = datetime.now().strftime("%m-%d-%Y") recipient_account_type = ( "Checking" if domestic_wire.recipient_account_type == "C" else "Savings" ) return { "transaction_id": domestic_wire.transaction_id, "aba": domestic_wire.intermed_aba or "", "payment_date": str(current_date), "currency": "USD", "amount": domestic_wire.transaction_amount, "description": domestic_wire.purpose_of_wire or "", "sender": { "cif": f"{source_account.cif_internal}", }, "receiver": { "account_num": domestic_wire.recipient_account, "account_type": recipient_account_type, "recipient_name": domestic_wire.recipient_name, "address": { "address1": domestic_wire.recipient_address1, "address2": domestic_wire.recipient_address2, "city": domestic_wire.recipient_city, "state": domestic_wire.recipient_state, "postal_code": domestic_wire.json.get("RecipientPostalCode", ""), }, "bank": { "name": domestic_wire.recipient_fi_name, "bank_id": domestic_wire.recipient_fi_aba, "address": { "address1": domestic_wire.recipient_fi_address1, "address2": domestic_wire.recipient_fi_address2, "city": domestic_wire.recipient_fi_city, "state": domestic_wire.recipient_fi_state, "postal_code": domestic_wire.recipient_fi_postal, }, }, }, }
Explanation:
This function constructs a JSON object that contains all the necessary details for the wire transfer, such as the transaction ID, payment date, sender and receiver information, and bank details.
The
send_wire_transfer
method is responsible for making the API call to the external wire provider (e.g.https://local-dev-api.q2developer.com/projects/sendDomesticWire
). It takes the serialized payload as input, specifies the wire provider’s URL and includes the necessary authentication headers (API key and headers in this example) to authenticate the request. The response is parsed and returned as a dictionary.async def send_wire_transfer(self, payload: dict) -> dict: url = "https://local-dev-api.q2developer.com/projects/sendDomesticWire" self.logger.info(f"payload: {payload}") response = await q2_requests.post( self.logger, url=url, headers={ "apikey": "S6TczFkiWjDZy7FElvqKYMogzdcEcoQy", "Content-Type": "application/json", }, data=json.dumps(payload), ) response_as_dict = response.json() self.logger.info(f"response: {response_as_dict}") return response_as_dict
async def handle_domestic_wire( self, domestic_wire: DomesticWireRequest, source_account: HostAccountRequest ) -> bool: """ Perform your domestic wire transfer here. If you return true, a transaction will be generated and visible throughout the q2 platform-- only return 'true' on a success. :param domestic_wire: Wire destination and details submitted by user :param source_account: The account from which to source the transfer """ payload = serialize_payload_to_json(domestic_wire, source_account) response = await self.send_wire_transfer(payload) if not response.get("success"): return False return True async def send_wire_transfer(self, payload: dict) -> dict: url = "https://local-dev-api.q2developer.com/projects/wireTransfer" self.logger.info(f"payload: {payload}") response = await q2_requests.post( self.logger, url=url, headers={ "apikey": "S6TczFkiWjDZy7FElvqKYMogzdcEcoQy", "Content-Type": "application/json", }, data=json.dumps(payload), ) response_as_dict = response.json() self.logger.info(f"response: {response_as_dict}") return response_as_dict def serialize_payload_to_json( domestic_wire: DomesticWireRequest, source_account: HostAccountRequest ): current_date = datetime.now().strftime("%m-%d-%Y") recipient_account_type = ( "Checking" if domestic_wire.recipient_account_type == "C" else "Savings" ) return { "transaction_id": domestic_wire.transaction_id, "aba": domestic_wire.intermed_aba or "", "payment_date": str(current_date), "currency": "USD", "amount": domestic_wire.transaction_amount, "description": domestic_wire.purpose_of_wire or "", "sender": { "cif": f"{source_account.cif_internal}", }, "receiver": { "account_num": domestic_wire.recipient_account, "account_type": recipient_account_type, "recipient_name": domestic_wire.recipient_name, "address": { "address1": domestic_wire.recipient_address1, "address2": domestic_wire.recipient_address2, "city": domestic_wire.recipient_city, "state": domestic_wire.recipient_state, "postal_code": domestic_wire.json.get("RecipientPostalCode", ""), }, "bank": { "name": domestic_wire.recipient_fi_name, "bank_id": domestic_wire.recipient_fi_aba, "address": { "address1": domestic_wire.recipient_fi_address1, "address2": domestic_wire.recipient_fi_address2, "city": domestic_wire.recipient_fi_city, "state": domestic_wire.recipient_fi_state, "postal_code": domestic_wire.recipient_fi_postal, }, }, }, }
Testing the Domestic Wire Transfer
Now that we have implemented the logic for the wire transfer, let’s test it within the Q2 Online Banking sandbox environment.
Log in to your sandbox environment: Use
commercial0
as the user login for testing or another user under theCommercial
group.Navigate to the Domestic Wire interface: Go to
Payments
>New Payment
>Domestic Wire
.Fill out the form: Enter the details for the domestic wire transfer, such as the account details, process date, recipient account, and transfer amount.
Approve the transaction: After filling out the form, click on
Approve
to initiate the transfer.When the end user clicks
Approve
, several actions occur behind the scenes to complete the transfer.The request is sent to your
handle_international_wire
method, which processes the data and prepares the payload for submission to the external wire transfer network.Your custom logic validates the transfer data, and handles the call to the third-party wire provider.
Depending on the response, your function will return a boolean to denote a successful or failed transaction
More details on the flow can be found in the Architecture section.