International Wire
The International Wire Adapter facilitates wire transfers from a U.S. financial institution to foreign institutions.
With the International Wire Adapter, users can:
Initiate one-time international wire transfers to recipients in any foreign financial institution.
Schedule future-date wire transfers for specific payment dates or set up recurring wire transfer series for regular payments.
Make SWIFT wire transfers
By default, the following boilerplate code will be generated in your extension:
async def handle_international_wire(
self, international_wire: InternationalWireRequest, source_account: HostAccountRequest
) -> bool:
international_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 returns 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 international wire transfer, add validations, and interact with third-party providers.
Walkthrough
Now, let’s walk through an example implementation for handling international wire transfers.
We’ll explain the key steps involved in the logic, starting with the handle_international_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_international_wire
method is the entry point for processing an international wire transfer. It takes two key parameters:international_wire
andsource_account
, which contain the wire details and the account information, respectively.async def handle_international_wire( self, international_wire: InternationalWireRequest, source_account: HostAccountRequest, ) -> bool: """ Perform your international 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 international_wire: Wire destination and details submitted by user :param source_account: The account from which to source the transfer """ # Serialize the wire transfer data to JSON format payload = await self.serialize_payload_to_json( international_wire, source_account ) # Send the transfer request to the external wire provider response = await self.send_wire_transfer(payload) # Handle the response: Log and return success/failure if not response.get("success"): self.logger.error(f"Error initiating wire transfer: {response}") return False self.logger.info(f"International wire transfer successful: {response}") return True
Explanation:
The
handle_international_wire
method is responsible for orchestrating the international 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.async def serialize_payload_to_json( self, international_wire: InternationalWireRequest, source_account: HostAccountRequest, ): current_date = datetime.now().strftime("%m-%d-%Y") recipient_account_type = ( "Checking" if international_wire.recipient_account_type == "C" else "Savings" ) amount_in_usd = international_wire.transaction_amount if international_wire.currency_code != "USD": amount_in_usd = ( international_wire.transaction_amount * international_wire.exchange_rate ) is_iban = False if international_wire.recipient_fi_swift_or_bic else True country_code = await self.get_country_code( international_wire.recipient_country_id ) return { "transaction_id": international_wire.transaction_id, "payment_date": str(current_date), "amount": amount_in_usd, "currency": "USD", "speed": "Express", "description": international_wire.purpose_of_wire or "", "sender": { "cif": f"{source_account.cif_internal}", }, "receiver": { "account_num": international_wire.recipient_account, "account_type": recipient_account_type, "recipient_name": international_wire.recipient_name, "currency": "USD", "name": international_wire.recipient_name, "address": { "address_line": international_wire.recipient_address1, "country_code": country_code, }, "bank": { "bank_id_type": "SWIFT" if not is_iban else "NID", "country_code": country_code, "swift_code": ( international_wire.recipient_fi_swift_or_bic if not is_iban else "" ), }, }, }
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.
async def get_country_code(self, country_id): countries = await self.db.country.get() country_code = None for country in countries: if country_id == country.CountryID.pyval: country_code = country.IsoCodeA2 return country_code
The
get_country_code
method retrieves the country code for the recipient’s country using the recipient’scountry_id
. This ensures that the correct country code is included in the payload when sending the wire transfer.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/sendInternationalWire
). 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): url = "https://local-dev-api.q2developer.com/projects/sendInternationalWire" 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_international_wire( self, international_wire: InternationalWireRequest, source_account: HostAccountRequest, ) -> bool: """ Perform your international 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 international_wire: Wire destination and details submitted by user :param source_account: The account from which to source the transfer """ # Serialize the wire transfer data to JSON format payload = await self.serialize_payload_to_json( international_wire, source_account ) # Send the transfer request to the external wire provider response = await self.send_wire_transfer(payload) # Handle the response: Log and return success/failure if not response.get("success"): self.logger.error(f"Error initiating wire transfer: {response}") return False self.logger.info(f"International wire transfer successful: {response}") return True async def serialize_payload_to_json( self, international_wire: InternationalWireRequest, source_account: HostAccountRequest, ): current_date = datetime.now().strftime("%m-%d-%Y") recipient_account_type = ( "Checking" if international_wire.recipient_account_type == "C" else "Savings" ) amount_in_usd = international_wire.transaction_amount if international_wire.currency_code != "USD": amount_in_usd = ( international_wire.transaction_amount * international_wire.exchange_rate ) is_iban = False if international_wire.recipient_fi_swift_or_bic else True country_code = await self.get_country_code( international_wire.recipient_country_id ) return { "transaction_id": international_wire.transaction_id, "payment_date": str(current_date), "amount": amount_in_usd, "currency": "USD", "speed": "Express", "description": international_wire.purpose_of_wire or "", "sender": { "cif": f"{source_account.cif_internal}", }, "receiver": { "account_num": international_wire.recipient_account, "account_type": recipient_account_type, "recipient_name": international_wire.recipient_name, "currency": "USD", "name": international_wire.recipient_name, "address": { "address_line": international_wire.recipient_address1, "country_code": country_code, }, "bank": { "bank_id_type": "SWIFT" if not is_iban else "NID", "country_code": country_code, "swift_code": ( international_wire.recipient_fi_swift_or_bic if not is_iban else "" ), }, }, } async def send_wire_transfer(self, payload): url = "https://local-dev-api.q2developer.com/projects/sendInternationalWire" 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 get_country_code(self, country_id): countries = await self.db.country.get() country_code = None for country in countries: if country_id == country.CountryID.pyval: country_code = country.IsoCodeA2 return country_code
Testing the International Wire Transfer
Now that we have implemented the logic for the international 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 International Wire interface: Go to
Payments
>New Payment
>International Wire
.Fill out the form: Enter the details for the international 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.