Integrating Mercoa with your BillPay / AP System

Overview

Mercoa’s Virtual Card Agent seamlessly integrates into your existing BillPay / AP workflows to automatically process invoice payments using virtual cards. It’s designed to automatically pay invoices using virtual cards whenever it’s possible and cost-effective. The integration includes a straightforward decision-based flow with a fallback to your current payment system, ensuring no disruption.

Integration Flow

This process is handled in two main phases: validation and processing. Your system first checks if an invoice is eligible for card payment and then acts on that information.

Validation Flow

This chart shows the initial decision-making process.

Processing Flow

If an invoice is cleared for card payment, this is the next step.

Implementation Steps

1. Trigger the Validation

When a payment is scheduled in your system, make a call to the Mercoa Agent’s validation endpoint. This is the entry point to the workflow.

1// Example: When payment is scheduled
2async function schedulePayment(invoice) {
3 // Your existing payment scheduling logic
4 const scheduledPayment = await createScheduledPayment(invoice);
5
6 // Call Mercoa Agent validation
7 const validationResult = await validateWithMercoaAgent(invoice);
8
9 // Store validation result for later use
10 await storeValidationResult(scheduledPayment.id, validationResult);
11
12 return scheduledPayment;
13}

2. Call the Validation Endpoint

Use the Mercoa Agent validation endpoint to check if the vendor’s invoice can be processed with a virtual card.

POST
/payment-gateway/validate
1from mercoa import Mercoa
2from mercoa.payment_gateway_types import ValidatePaymentGatewayRequest_Document
3
4client = Mercoa(
5 token="YOUR_TOKEN",
6)
7client.payment_gateway.create_validation_job(
8 request=ValidatePaymentGatewayRequest_Document(
9 document="data:application/pdf;base64,JVBERi0xLjEKJcKlwrHDqwoKMSAwIG9iagogIDw8IC9UeXBlIC9DYXRhbG9nCiAgICAgL1BhZ2VzIDIgMCBSCiAgPj4KZW5kb2JqCgoyIDAgb2JqCiAgPDwgL1R5cGUgL1BhZ2VzCiAgICAgL0tpZHMgWzMgMCBSXQogICAgIC9Db3VudCAxCiAgICAgL01lZGlhQm94IFswIDAgMzAwIDE0NF0KICA+PgplbmRvYmoKCjMgMCBvYmoKICA8PCAgL1R5cGUgL1BhZ2UKICAgICAgL1BhcmVudCAyIDAgUgogICAgICAvUmVzb3VyY2VzCiAgICAgICA8PCAvRm9udAogICAgICAgICAgIDw8IC9GMQogICAgICAgICAgICAgICA8PCAvVHlwZSAvRm9udAogICAgICAgICAgICAgICAgICAvU3VidHlwZSAvVHlwZTEKICAgICAgICAgICAgICAgICAgL0Jhc2VGb250IC9UaW1lcy1Sb21hbgogICAgICAgICAgICAgICA+PgogICAgICAgICAgID4+CiAgICAgICA+PgogICAgICAvQ29udGVudHMgNCAwIFIKICA+PgplbmRvYmoKCjQgMCBvYmoKICA8PCAvTGVuZ3RoIDU1ID4+CnN0cmVhbQogIEJUCiAgICAvRjEgMTggVGYKICAgIDAgMCBUZAogICAgKEhlbGxvIFdvcmxkKSBUagogIEVUCmVuZHN0cmVhbQplbmRvYmoKCnhyZWYKMCA1CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxOCAwMDAwMCBuIAowMDAwMDAwMDc3IDAwMDAwIG4gCjAwMDAwMDAxNzggMDAwMDAgbiAKMDAwMDAwMDQ1NyAwMDAwMCBuIAp0cmFpbGVyCiAgPDwgIC9Sb290IDEgMCBSCiAgICAgIC9TaXplIDUKICA+PgpzdGFydHhyZWYKNTY1CiUlRU9GCg==",
10 ),
11)
1import { MercoaClient } from "@mercoa/javascript"
2
3const mercoa = new MercoaClient({
4 token: "YOUR_API_KEY",
5})
6
7async function validateWithMercoaAgent(invoice) {
8 const validationJob = await mercoa.paymentGateway.createValidationJob({
9 type: "html",
10 html: `<html><body><h1>Invoice ${invoice.id}</h1><p>Amount: $${invoice.amount}</p></body></html>`
11 })
12
13 return validationJob
14}

3. Analyze the Validation Response

The validation endpoint returns a response indicating whether the invoice can be paid with a card and lists any applicable fees.

  • Whether the invoice can be processed with a virtual card
  • The processing fee (if applicable)
  • Any additional requirements or restrictions
1{
2 "canProcessWithCard": true,
3 "processingFee": 2.50,
4 "feeCurrency": "USD",
5 "restrictions": [],
6 "estimatedProcessingTime": "1-2 business days"
7}

4. Apply Your Decision Logic

Based on the validation response, implement logic on your side to decide the next step.

  • If card processing isn’t available, fall back to your current workflow.

  • If it is available, check if the fee is within your acceptable threshold. If not, fall back.

  • If the fee is acceptable, proceed to process the payment with a virtual card.

1async function processPaymentDecision(invoice, validationResult) {
2 // If card processing is not available, use current workflow
3 if (validationResult.card?.eligibility !== 'ACCEPTED') {
4 return await processWithCurrentWorkflow(invoice);
5 }
6
7 // Check if fee is acceptable (implement your threshold logic)
8 const feeThreshold = getFeeThreshold(invoice.amount);
9 const processingFee = validationResult.card?.fee?.value || 0;
10 if (processingFee > feeThreshold) {
11 return await processWithCurrentWorkflow(invoice);
12 }
13
14 // Proceed with virtual card processing
15 return await processWithVirtualCard(invoice, validationResult);
16}

5. Process the Payment

If the decision is to use a virtual card, call Mercoa process endpoint. Your implementation should handle both success and failure. If the payment fails for any reason, fall back to your existing workflow.

1async function processWithVirtualCard(invoice, validationResult) {
2 try {
3 const processJob = await mercoa.paymentGateway.createProcessJob({
4 type: "html",
5 html: `<html><body><h1>Invoice ${invoice.id}</h1><p>Amount: $${invoice.amount}</p></body></html>`,
6 cardDetails: {
7 type: "stripeIssuing",
8 firstName: "John",
9 lastName: "Doe",
10 postalCode: "12345",
11 country: "US",
12 stripeCardId: "ic_1234567890abcdef",
13 stripePublishableKey: "pk_test_1234567890abcdef",
14 ephemeralKeyEndpoint: {
15 url: "https://api.example.com/ephemeral-keys",
16 method: "POST",
17 headers: {
18 "Authorization": "Bearer YOUR_AUTH_SCHEME",
19 "Content-Type": "application/json"
20 },
21 postBody: "{\"card_id\": \"{{cardId}}\", \"nonce\": \"{{nonce}}\", \"account_id\": \"{{accountId}}\"}"
22 }
23 }
24 })
25
26 if (processJob.jobStatus === 'success') {
27 // Reconcile the payment with your invoice
28 await reconcilePayment(invoice.id, processJob.receiptUrl);
29 return { success: true, method: 'virtual_card' };
30 } else {
31 // Fall back to current workflow
32 return await processWithCurrentWorkflow(invoice);
33 }
34 } catch (error) {
35 // Handle errors and fall back to current workflow
36 console.error('Virtual card processing failed:', error);
37 return await processWithCurrentWorkflow(invoice);
38 }
39}

6. Reconcile the Payment

When a virtual card payment succeeds, you must reconcile the payment on your side. This typically means updating your invoice’s status to “paid” and storing the transaction details for your records.

1async function reconcilePayment(invoiceId, receiptUrl) {
2 // Update your invoice status to paid
3 await updateInvoiceStatus(invoiceId, 'paid');
4
5 // Store transaction details for audit trail
6 await storeTransactionDetails(invoiceId, receiptUrl, {
7 method: 'virtual_card',
8 processedAt: new Date(),
9 // Include other relevant transaction data
10 });
11
12 // Trigger any post-payment workflows
13 await triggerPostPaymentWorkflows(invoiceId);
14}

Migration Strategy

We recommend a phased approach to roll out the integration.

Phase 1: Validation Only

  • Implement the validation endpoint call.

  • Log the results to analyze vendor eligibility rates and potential fees without actually processing payments.

Phase 2: Limited Processing

  • Enable virtual card processing for a small, selected group of low-risk vendors or invoices.

  • Monitor success rates and ensure your reconciliation logic works as expected.

Phase 3: Full Integration

  • Enable the workflow for all eligible invoices.

  • Ensure your fallback mechanisms are robust.

  • Continuously monitor and optimize based on performance data.