Payout Merchants

 

Your platform must deliver Payout management UIs to merchants who were not onboarded with access to the WePay Merchant Center. At a high-level, delivering Payout UIs requires the following capabilities:

  • Create Payout Methods
  • Tie Payout Methods to merchants' WePay Accounts
  • Get API Notifications when a Payout is automatically created
  • Allow merchants to update their Payout Method details and preferences

Create a Payout Method

Now that you understand how to payout merchants, let's walk through how to build your Payout flow. Be sure to grab your merchant's Legal Entity ID and Account ID from the Onboard Merchants section. Like the previous two articles, we recommend using the Tokenization strategy for collecting sensitive information, like a merchant's payment bank information.

First, we'll need to make sure that the merchant's Legal Entity and Account have all the necessary information to enable Payouts. As a reminder, we had to add extra information to begin processing payments; similar information is required to send payouts. Start by doing the following:

GET /legal_entities/{id}/verifications

You'll notice what information is missing before the Legal Entity can enable payouts. In order to fully unlock payouts, the information documented at Enable Payouts needs to be provided.  

Once that is completed, send the equivalent call for Accounts:

GET /accounts/{id}/capabilities

If you successfully added the missing Legal Entity information, you'll see the Payouts section enabled to true in the Accounts response. If you don't, use the response to fill in information to update the Legal Entity and Account.

Let's now collect Payout Information for merchants. We'll start with the front end, where a merchant will enter their payout bank information. In your UI, be sure to collect the payer's decision to use a bank account for use later in this process. You'll want to create fields that capture the information below, using the Tokenization JS library:

Copy
Copied
WePay.tokens.create({
"resource": "payout_methods",
"payout_methods": {
  		"type": "payout_bank_us",
    		"payout_bank_us": {
    			"account_number": "12345678",
"routing_number": "021000021",
			"account_type": "checking"
}
  	}
}, {}, function(response) {
  console.log(response);
});
This will return a token. Using the token, create a Payout Method by sending a POST /payout_methods request, similar to the one below. The field nickname should not contain any sensitive data, such as any part of the bank account number other than the last 4 digits.
Copy
Copied
curl -X POST \
  --url 'https://stage-api.wepay.com/payout_methods' \
  -H 'Accept: application/json'\
  -H 'App-Id: {your-app-id}'\
  -H 'App-Token: {your-app-token}'\
  -H 'Api-Version: 3.0'\
  -H 'Content-Type: application/json' \
  --data-raw '{
	"legal_entity_id": "{YOUR-LEGAL-ENTITY-ID-HERE}",
	"nickname": "Test Payout Account",
	"token": {
		"id": "payout_methods-88ae04ca-0983-468b-9ca6-2ce18b66dafb"
	}
}'

We've provided a sample UI to simulate creating and tokenizing a Payout Method, see below. This UI example allows a merchant to provide their Payout bank information, in which a Payout Method token will be created and returned.

csshtmljs
Copy
Copied
#shipping-address {
  display: none;
}

#complete-screen {
  display: none;
}
Copy
Copied
<body>

  <div class="container">
    <ul class="collapsible popout">
      <li>
        <div class="collapsible-header"><i class="material-icons">attach_money</i>Payout Bank Account Currency Form
        </div>

        <div class="collapsible-body" id="order-body">
          <div class="container">
            <div class="row">
              <div class="input-field col s12">
                <select id="currencySelect">
                  <option value="" disabled selected>Choose Your Bank Currency</option>
                  <option value="1">United States</option>
                  <option value="2">Great Britain</option>
                  <option value="3">Canada</option>
                </select>
              </div>
            </div>
            <div class="row">
              <button id="nextButton" onClick="showMe()" type="submit" form="form1" value="Next">Next</button>
            </div>
          </div>
        </div>
      </li>
      <li>
        <div class="collapsible-header"><i class="material-icons">account_balance</i>Payout Method Bank Information
        </div>
        <div class="collapsible-body row" id="account-body">
          <div class="row">
            <div class="col s12">
              <div class="card blue-grey">
                <div class="card-content white-text">
                  <span class="card-title">Bank Account Information</span>
                  <p>Enter your Payout Method Bank Account Information</p>
                </div>
              </div>
            </div>
          </div>
          <div id="payouts" class="container">

            <div id="payoutTypes">

              <div id="payout_bank_ca" style="display:none">
                <form class="col s12">
                  <div class="row">
                    <div class="input-field col s12">
                      <input placeholder="institution number" id="institution_number" type="text" class="validate">
                      <label for="institution number">Institution Number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="transit number" id="transit_number" type="text" class="validate">
                      <label for="transit number">transit number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="account number" id="account_number_ca" type="text" class="validate">
                      <label for="account_number">account number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="account number" id="re_enter_account_number_ca" type="text" class="validate">
                      <label for="account_number">re-enter account number</label>
                    </div>
                  </div>
                  <p>Select Account Type</p>
                  <form action="#" id="accountType">
                    <p>
                      <label>
                        <input name="group1" id="checking_ca" type="radio" />
                        <span>Checking</span>
                      </label>
                    </p>
                    <p>
                      <label>
                        <input name="group1" id="savings_ca" type="radio" />
                        <span>Savings</span>
                      </label>
                    </p>
                  </form>

                </form>
              </div>

              <div id="payout_bank_us" style="display:none">
                <form class="col s12">
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="routing number" id="routing_number_us" type="text" class="validate">
                      <label for="routing number">routing number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="re-enter routing number" id="re_enter_routing_number_us" type="text"
                        class="validate">
                      <label for="routing number">re-enter routing number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="account number" id="account_number_us" type="text" class="validate">
                      <label for="account_number">account number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="re-enter account number" id="re_enter_account_number_us" type="text"
                        class="validate">
                      <label for="account_number">re-enter account number</label>
                    </div>
                  </div>
                  <p>Select Account Type</p>
                  <form action="#" id="accountType">
                    <p>
                      <label>
                        <input name="group1" id="checking_us" type="radio" />
                        <span>Checking</span>
                      </label>
                    </p>
                    <p>
                      <label>
                        <input name="group1" id="savings_us" type="radio" />
                        <span>Savings</span>
                      </label>
                    </p>
                  </form>

                </form>
              </div>

              <div id="payout_bank_gb" style="display:none">
                <form class="col s12">

                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="account number" id="account_number_gb" type="text" class="validate">
                      <label for="account_number">account number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="account number" id="re_enter_account_number_gb" type="text" class="validate">
                      <label for="account_number">re-enter account number</label>
                    </div>
                  </div>
                  <div class="row">
                    <div class="input-field col s6">
                      <input placeholder="sort code" id="sort_code" type="text" class="validate">
                      <label for="sort_code">sort code</label>
                    </div>
                  </div>
                </form>
              </div>
            </div>

            <button id="submit-payout-bank-button" class="btn waves-effect waves-light" type="submit"
              name="action">Submit
              <i class="material-icons right">send</i>
            </button>
          </div>

        </div>
      </li>
    </ul>
    <div id="results"></div>
    <script src="https://cdn.wepay.com/wepay.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</body>
Copy
Copied
var myAppId = "{your-app-id}" //assign your app ID

document.addEventListener('DOMContentLoaded', function() {
  // initialize materialize collapsible
  var collapsibleElement = document.querySelector('.collapsible');
  var collapsibleInstance = M.Collapsible.init(collapsibleElement); 

  // load with Payout Bank Curreny Type open
  collapsibleInstance.open(0);
  
  var elems = document.querySelectorAll('select');
  var instances = M.FormSelect.init(elems);
});
// show shipping address if different
function showMe() {

  var currencyType = document.getElementById('currencySelect');
  var bankCurrency = currencyType.options[currencyType.value].innerHTML;
 
  if (bankCurrency == "Great Britain") {
     document.getElementById('payout_bank_gb').style.display = "block";
  } else if (bankCurrency == "Canada") {
     document.getElementById('payout_bank_ca').style.display = "block";
     // document.getElementById('accountNumber').style.display = "block";
  } else if ( bankCurrency == "United States") {
      document.getElementById('payout_bank_us').style.display = "block";
  }

  
    var elems = document.querySelector('.collapsible');
  var instances = M.Collapsible.init(elems);
  instances.close(0);
  var elems = document.querySelector('.collapsible');
  var instances = M.Collapsible.init(elems);
  instances.open(1);
}


var apiVersion = "3.0";
var error = WePay.configure("stage", myAppId, apiVersion);
if (error) {
  console.log(error);
}

document.getElementById('submit-payout-bank-button').addEventListener('click', function (event) {
  
var rn = document.getElementById("re_enter_routing_number_us").value
var rern = document.getElementById("routing_number_us").value
var an = document.getElementById("re_enter_account_number_us").value
var rean = document.getElementById("account_number_us").value
var an_gb = document.getElementById("account_number_gb").value
var anGB = document.getElementById("re_enter_account_number_gb").value
var an_ca = document.getElementById("account_number_ca").value
var anCA = document.getElementById("re_enter_account_number_ca").value

  if (rn != rern) {
    throw 'Routing numbers do not match';
  }
  
  if (an != rean) {
    throw 'Account numbers do not match';
  }
  
  if (anGB != an_gb) {
    throw 'Routing numbers do not match';
  }
  
  if (anCA != an_ca) {
    throw 'Routing numbers do not match';
  }
  
  
      const payoutBank = {
        "United States": "payout_bank_us",
        "Canada": "payout_bank_ca",
        "Great Britain": "payout_bank_gb"
      }
      var currencyType = document.getElementById('currencySelect');
      var bankCurrency = String(currencyType.options[currencyType.value].innerHTML);
      var payout = payoutBank[bankCurrency];
      
      var accountNumberUS = document.getElementById("account_number_us").value;
      var accountNumberGB = document.getElementById("account_number_gb").value;
      var accountNumberCA = document.getElementById("account_number_ca").value;
      var routingNumber = document.getElementById("routing_number_us").value;
      var sortCode = document.getElementById("sort_code").value;
      var institutionNumber = document.getElementById("institution_number").value;
      var transitNumber = document.getElementById("transit_number").value;
  
      var accountType;
  
      if(document.getElementById('checking_us').checked || document.getElementById('checking_ca').checked) {
        accountType = "checking"
      }else if(document.getElementById('savings_us').checked || document.getElementById('savings_ca').checked) {
        accountType = "savings"
      }
      
  
      var payoutData;
      switch(payout) {
        case "payout_bank_ca":
          payoutData = {
            "resource": "payout_methods",
            "payout_methods": {
                "type": "payout_bank_ca",
                "payout_bank_ca": {
                "account_number": accountNumberCA,
                "transit_number": transitNumber,
                "institution_number": institutionNumber,
                "account_type": accountType
              }
           }
        }
        break;
        case "payout_bank_gb":
        payoutData = {
                      "resource": "payout_methods",
                      "payout_methods": {
                        "type": "payout_bank_gb",
                        "payout_bank_gb": {
                          "account_number": accountNumberGB,
                          "sort_code": sortCode
              }
           }
        }
         break;
        case "payout_bank_us":
        payoutData = {
                    "resource": "payout_methods",
                    "payout_methods": {
                      "type": "payout_bank_us",
                    "payout_bank_us": {
                        "account_number": accountNumberUS,
                        "routing_number": routingNumber,
                        "account_type": accountType
              }
           }
        }
    }
    WePay.tokens.create(payoutData, {}, function(response) {
       // display the response on the page for testing purposes; do not launch with this section
      var node = document.createElement('div');
      node.innerHTML = JSON.stringify(response)
      document.getElementById('results').appendChild(node)
    });
});

At this point, the API will return a Payout Method ID, which is linked to a Legal Entity. Keep reference to the Payout Method ID, as we'll connect it back to an Account, and attach a Payout schedule.

Note

If a duplicate Payout Method is created (i.e. identical account number, routing number, and nick name), a new Payout Method ID will be returned. On the back end, the original and new Payout Method ID(s) will point to the same object. Put into practice, this means you should fetch Payouts based on the merchant's Account ID instead of Payout Method ID if they have multiple Payout Methods associated with their Legal Entity.


Attach a Payout Method to a Merchant Account

With a Payout Method ID handy, it's time to connect an Account. First, determine which payout periods you will offer to your merchants. Note that it is a legal requirement to offer the same selection to all of your merchants. We currently support daily, weekly, and monthly payout schedules for bank account Payout Methods.

Once your Payout period offerings are established and selected by your merchant, either update the merchants existing Account by sending a POST /account/id request or create their Account if not previously done by sending a POST /accounts request. Our example below assumes that the merchant already has an API Account:
Copy
Copied
curl -X POST \
  --url 'https://stage-api.wepay.com/accounts/id' \
  -H 'Accept: application/json'\
  -H 'App-Id: {your-app-id}'\
  -H 'App-Token: {your-app-token}'\
  -H 'Api-Version: 3.0'\
  -H 'Content-Type: application/json' \
  --data-raw '{
  "payout": {
    "currencies": {
      "USD": {
        "payout_method_id": "{YOUR-PAYOUT-METHOD-ID}",
        "period": "weekly"
      }
    }
  }
}'

In case the account accepts multiple currencies, you will need to create multiple Payout Methods for each currency.


Remove a Payout Method from a Merchant Account

If a Payout Method ever needs to be removed from an Account (i.e. if the merchant submitted incorrect banking info but hasn't located the correct info yet), it's possible to remove the payout method by updating the account with a POST /account/id request like this:
Copy
Copied
{
  "payout": {
    "currencies": {
      "USD": null
    }
  }
}

Reconcile Payouts

As a WePay partner using the Clear product with custom components, you are required to build out reporting for your merchants. The simplest way to provide reconciliation reports is outlined here:

First, send a GET /payouts API request with the query parameter owner_id. The value here will be the merchant's Account ID. The response body will contain an array of Payouts for that Account.

Note that, if not provided, the create_time_start query parameter defaults to 7 days in the past, and the create_time_end query parameter defaults to the time of the request. Additionally, the delta between create_time_start and create_time_end cannot exceed 35 days.

Next, compile a report containing all transactional resources (Payments, Refunds, Disputes, Adjustments, Recoveries, and Payouts) in chronological order.

To read more about building out reports for your merchants, take a look at our Reporting article.