Village Product and Developer Docs
  • 📘Welcome to Village Labs
    • Welcome to Village Labs
    • Self Service Onboarding
    • Help Pages
      • Adding and Deleting Users
      • Mapping Users to Source tool IDs
      • How to follow or unfollow other users
      • Configuring & Removing Daily Slack Notification Whitelist
      • Changing the Reporting Lines & Teams
      • Following Custom Reports
      • Google Drive Privacy: When will my documents appear in Village Reports?
    • Custom Reports
      • Creating Team Reports
      • Creating Custom Reports & Custom Prompt Library
    • Meetings
      • Connecting your Calendar
      • Configuring your Meetings
      • Meeting Summary Reports
    • Village Assistant
      • Github PR Review Tool
    • Security & Compliance
    • Data Privacy & Permissions
  • 🔌Data Connections
    • Airtable
    • Ashby
    • Clickup
      • Finding ClickUp User IDs
    • Figma
    • Github
      • Finding GitHub User IDs
    • Gitlab
    • Google - OAuth (Recommended)
    • Google - Manual Connection
    • Hubspot
    • Jira
      • Finding Jira User IDs
    • Confluence
      • Finding Confluence / Jira User IDs
    • Linear
    • Asana
    • Monday
    • Notion
    • Pipedrive
    • Slack
    • Basecamp
    • Zoom
  • 👩‍💻Legacy Developer Docs
    • Developer Quickstart
    • Village APIs: Introduction
      • Activity API
      • User Status API
      • Patch User API
      • Segments API
      • Redemption API
      • Master Award Controls
      • Connections (Referrals) APIs
      • GET APIs
    • 3rd Party Payments Integrations
    • Embedding Village Dashboards
  • 🕵️LEGACY Knowledge Base
    • Admin Quickstart
    • Referrals Support
    • The Basics of Village in 15 Minutes
    • Programs & Rules
      • Triggers
        • Activities
        • Goals
      • Conditions
        • Segment Conditions
        • Time Conditions
        • Max Budget Conditions
        • Conditional Multipliers
    • Awards
      • Monetary (Cash) Awards
      • Non-Monetary Awards
        • Funding
          • In-depth use cases for funding pools
      • Badges & Statuses
      • Award Expiration
    • Segments
      • Segmentation Use Cases
    • Rule Evaluation Logic Deep-dive
    • The Village Dashboards
      • Admin Account Creation
      • User Access Management
      • Network Settings
      • User Dashboard
    • BigQuery
    • Managing Payment Integrations
    • Referrals (Connections)
      • Pre-populating Users' Unique Referral Codes In Signup Flows & Forms
  • No and Low Code Solutions
    • No and Low Code Solutions
      • No & Low Code Solutions
  • Feedback
    • Village Docs Feedback Form
    • Feature Requests
    • Talk to our Team
Powered by GitBook
On this page
  • About Cash Outs
  • Stripe & PayPal Apps
  • Use incoming webhooks to initiate third party payouts
  • Configure Endpoint & Set Secret
  • Validating Payloads from Village
  • What to Expect in the Webhook
  • Your Expected Response

Was this helpful?

  1. Legacy Developer Docs

3rd Party Payments Integrations

Integrating with Stripe, PayPal, and other payment services

PreviousGET APIsNextEmbedding Village Dashboards

Last updated 1 year ago

Was this helpful?

About Cash Outs

Whenever you issue a Monetary (Cash) Award type to one of your users, they will have the option to "Cash Out" on their end-user dashboard. Depending on how you configure your Cash Award and the Third Party Payments associated with those Awards, Village will change how it handles end-user cash out requests for your Network.

There are multiple options for how Village will handle Cash Outs:

  1. Stripe App - best if you use Stripe and want to manage all payouts directly through Village

  2. PayPal App - best if you use PayPal and want to manage all payouts directly through Village

  3. Manual payouts - best if you're just getting started, or you primarily use ACH or wire transfers

  4. Webhooks for real-time payment initiation - best if your platform uses other 3rd-party payments providers and/or you want complete, automated control over payouts

Stripe & PayPal Apps

Village has built direct integrations with Stripe and PayPal that require a quick but secure integration with the Village engineering team. For security reasons, this process is not self service.

Reach out to our team at support@villagelabs.co to get the process started.

Use incoming webhooks to initiate third party payouts

Listen for events from Village so you can automatically trigger payouts in services like Stripe and PayPal.

Overview of webhook process:

  1. Configure webhook endpoint

  2. Set Secret

  3. Validate Payloads from Village

  4. Process Payouts Payload

  5. Send Response to Village

Configure Endpoint & Set Secret

To configure your webhook navigate to Side Menu -> Treasury -> Third Party Payments. Then select the Cash Award type you want to modify.

To configure your webhook endpoint, do the following:

  1. Primary Payout Method: Webhook

  2. HTTP Endpoint: https://your_endpoint_here

  3. Set Secret:

    1. In the "Secret" field, type a random string with high entropy. You can generate a string with ruby -rsecurerandom -e 'puts SecureRandom.hex(20)' in the terminal, for example.

  4. Click Save.

Next, set up an environment variable on your server that stores this token. Typically, this is as simple as running:

$ export SECRET_TOKEN=YOUR-TOKEN

Never hardcode the token into your app!

Validating Payloads from Village

When your secret token is set, Village uses it to create a hash signature with each payload. This hash signature is included with the headers of each request.

You should calculate a hash using your SECRET_TOKEN, and ensure that the result matches the hash from Village. Village uses an HMAC hex digest to compute the hash.

Note: Webhook payloads can contain unicode characters. If your language and server implementation specifies a character encoding, ensure that you handle the payload as UTF-8.

Note:

  • No matter which implementation you use, the hash signature starts with sha256=, using the key of your secret token and your payload body.

What to Expect in the Webhook

Village will pass the key cash out details to your platform so that you can easily initiate a payout using your third party payment provider's API.

// Webhook Model for Stripe and PayPal 

  return {
    amount: details.amount,
    currency: details.currency,
    destination_email: userData.walletAddress,
    metadata: {
      requested: moment().valueOf() / 1000,
      village_transfer_id: details.operationIdx,
      village_user_id: details.humanId,
      transaction_source: "village_cashout",
      suggested_description: "You have a payout from Village!",
    },
  };

Example of how to initiate a Stripe or PayPal payout using the Village Cash Payout webhook (Python).

In this code:

  1. Before initiating a payout, make sure the connected account has enabled payouts and the bank account or debit card is set up properly.

  2. Stripe expects the amount for its transactions in the smallest unit of whatever currency you're working with. So if you're dealing with dollars, you need to convert it to cents.

  3. The Stripe API needs the ID of the connected account to which you want to pay out. This ID usually starts with "acct_". You'll need to map the destination_email from your webhook to the corresponding Stripe Account ID.

  4. For the metadata field, you can pass an optional dictionary of additional Stripe API parameters.

  5. Stripe's Payout API also includes an optional description field where you can put a human-readable description for the payout.

  6. You should handle exceptions that could be raised during the creation of the payout, such as InvalidRequestError, AuthenticationError, APIConnectionError, StripeError, etc., depending on your needs.

You need to replace os.getenv('STRIPE_SECRET_KEY') with your Stripe secret key. It's not safe to hard code your key into your source code, so you should store it in an environment variable or some secure method.

// Python example

import stripe
import os

stripe.api_key = os.getenv('STRIPE_SECRET_KEY') # Replace this with your Stripe secret key

# Information received from the Village Cash Payouts webhook
webhook_data = {
    "amount": details.amount,
    "currency": details.currency,
    "destination_email": userData.walletAddress,
    "metadata": {
      "requested": moment().valueOf() / 1000,
      "village_transfer_id": details.operationIdx,
      "village_user_id": details.humanId,
      "transaction_source": "village_cashout",
      "suggested_description": "You have a payout from Village!",
    },
}

# You need to convert the amount to the smallest unit of the currency, i.e., cents for USD
amount_in_cents = int(webhook_data["amount"] * 100)

# Assume you have retrieved the correct Stripe Account ID connected with the `destination_email`
stripe_account_id = "acct_XXXXXXXXXXXXXX" 

payout = stripe.Payout.create(
    amount=amount_in_cents,
    currency=webhook_data["currency"],
    metadata=webhook_data["metadata"],
    description=webhook_data["metadata"]["suggested_description"],
    stripe_account=stripe_account_id,
)

print(payout)

In this code:

  1. You first authenticate with PayPal's API to get an access token.

  2. You then send a POST request to PayPal's payouts endpoint to create a payout.

  3. The sender_batch_header object has an email_subject which is the subject of the email that the recipient sees.

  4. The items list can contain information about multiple payouts, but in this case, it only contains one payout.

  5. The recipient_type is set to EMAIL, and the receiver is set to the recipient's email address.

  6. note is a custom note that will be associated with the payout.

  7. sender_item_id is a custom ID for tracking the payout. You can set this to a unique ID for tracking purposes.

Remember to replace 'PAYPAL_CLIENT_ID' and 'PAYPAL_SECRET' with your actual PayPal Client ID and Secret. These should not be hard-coded in your program and instead be stored securely.

// PayPal example

import requests
import os
import json

# Obtain access token
url = "https://api.sandbox.paypal.com/v1/oauth2/token" # use https://api.paypal.com/v1/oauth2/token for production

auth = (os.getenv('PAYPAL_CLIENT_ID'), os.getenv('PAYPAL_SECRET')) # Replace with your PayPal Client ID and Secret
headers = {'Accept': 'application/json', 'Accept-Language': 'en_US'}
data = {'grant_type': 'client_credentials'}

response = requests.post(url, headers=headers, data=data, auth=auth)
token = response.json()['access_token']

# Information received from the webhook
webhook_data = {
    "amount": details.amount,
    "currency": details.currency,
    "destination_email": userData.walletAddress,
    "metadata": {
      "requested": moment().valueOf() / 1000,
      "village_transfer_id": details.operationIdx,
      "village_user_id": details.humanId,
      "transaction_source": "village_cashout",
      "suggested_description": "You have a payout from Village!",
    },
}

# Creating payout
payouts_url = 'https://api.sandbox.paypal.com/v1/payments/payouts' # use https://api.paypal.com/v1/payments/payouts for production
headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + token,
}
data = {
    "sender_batch_header": {
        "email_subject": webhook_data["metadata"]["suggested_description"]
    },
    "items": [
        {
            "recipient_type": "EMAIL",
            "amount": {
                "value": webhook_data["amount"],
                "currency": webhook_data["currency"]
            },
            "receiver": webhook_data["destination_email"],
            "note": webhook_data["metadata"]["suggested_description"],
            "sender_item_id": webhook_data["metadata"]["village_transfer_id"]
        }
    ]
}

payout_response = requests.post(payouts_url, headers=headers, data=json.dumps(data))

print(payout_response.json())

Your Expected Response

Caution: If you have the Cash Payout webhook configured and Village does not receive a timely 200 Response, or if the request times out, the balance will be returned to the user and the Cash Out request will fail.

Expected Response Codes:

Code
Description

200

OK: Successfully Processed.

400

Bad Request: your server could not process the request due to malformed payload.

500

Internal Server Error: An unexpected error occurred on the server while processing the request.

If Village receives a 200 response code it will complete the end user's Cash Out request and remove the Monetary Award type from their digital wallet.

Using a plain == operator is not advised. A method like performs a "constant time" string comparison, which helps mitigate certain timing attacks against regular equality operators.

👩‍💻
secure_compare
Click for details
Cash Out Initiation Flow