Merchant Payments
Overview
Productfy offers the ability for you to accept credit and debit card payments in your app. The money from these payments can settle to your own account, or your customer's accounts seamlessly. Additionally, these payments can integrate with Productfy's Virtual Accounts if you want to build a real-time transfer of money between users on the platform.
Features
- Accept Visa, Mastercard, Discover and American Express card payments within your app
- Customizable payment form prevents your app from seeing card information and needing to be PCI compliant
- Optional integration with Productfy Virtual Accounts to facilitate real-time sender-to-receiver transfer of funds
- Optional ability to add fees to payments for an additional revenue stream
- Process refunds and view data from Productfy's Client Portal (coming soon)
Implementation Process
- Work with your Productfy representative to finalize contract terms
- Submit your merchant setup request here
- Obtain your Transcenter ID and Processor ID from the GoEmerchant portal, Security Settings tab - these values will be needed for your technical integration
- Follow the steps of the technical integration below
Testing Card Payment Acceptance
In the test environment you can submit payments directly to our API (see Step 2), however in production you must use the hosted iFrame to retrieve cryptograms to be used in place of card numbers in the API.
Step 1 - Integrate Hosted iFrame
You will display an iFrame in your app that generates a cryptogram for the card information that can be used in a subsequent API call to process payments.
You will need to include the iFrame as well as a script that will initialize the iframe. Use the URL, Transcenter ID, and Processor ID from the portal.
iFrame Code
<!-- <iframe id="firstpay-iframe"src="https://secure-v.goemerchant.com/secure/PaymentHostedForm/v3/CreditCard"data-transcenter-id="[Transcenter Id Goes Here]" data-processor-id="[Processor Id GoesHere]" data-transaction-type="Sale" data-manual-submit="false"></iframe>
Initialization Script
<scriptsrc="https://secure-v.1stpaygateway.net/secure/PaymentHostedForm/Scripts/firstpay/firstpay.cryptogram.js"id="firstpay-script-cryptogram" type="text/javascript" data-transcenter="[TransactionCenter Id goes here]" data-processor="[Processor Id goes here]"data-type="Sale"></script>
Customizing The iFrame
The javascript code sample above will loop through all of the stylesheets in your site and look for all instances of classes with firstpay in them. All the classnames inside of the iframe use selectors with firstpay in them so it’s possible to style the iframe content as long as you mount overriding stylesheet before the javascript initiates.
Class | Class Description |
---|---|
firstpay-container | The div element in the iFrame where the form element is loaded |
firstpay-form | The form element |
firstpay-form-row | The div element representing each field in the form and its label |
firstpay-label | Label element for a form input |
firstpay-input | Text input or select element |
firstpay-validation-error | Div element for validation errors for each firstpay-input element |
firstpay-button | Button element for manual form submits |
Sample Stylesheet
iframe#firstpay-iframe {border: none;height: 300px;}.firstpay-form-row input, .firstpay-form-row select {border: 1px solid #c3c3c3;border-radius: 4px;font-size: 16px;padding: 8px 6px;}.firstpay-form-row label {font-size: 14px;font-family: sans-serif;}.firstpay-form-row + .firstpay-form-row {margin-top: 8px;}
Communicating With The Form
After a cryptogram is generated you will need to submit the cryptogram to productfy using the Create Merchant Transaction API. The implementation details are up to your application’s workflow but the iFrame will emit message events that you can use to capture changes to the iFrame form state.
// Add event listener for dispatched message from iframe https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessagewindow.addEventListener("message", (event) => {// Check if event data exists and it is an event from the firstpay iframeif (event && event.data && event.data.firstpay) {switch(event.data.type) {/** These are the data types that the iframe will postMessage to* the parent window. Some of these may be useful to drive the UI* (e.g. loading indicators, error messages) At a minimum you will need to* handle `newCryptogram` which will contain the cryptogram that needs to* be sent to Productfy via [graphql mutation].*/case 'formReady':// form loaded and readybreak;case 'generatingCryptogram':// form page has sent a request to generate a cryptogrambreak;case 'newCryptogram':// cryptogram successfully generated; set cryptogram in form to submit to pfyconst { code, cryptogram, expMonth, expYear, first6, last4, success } = event.data;if (!success) {// show client error//setAppError('Generic error message');}// Store cryptogram to send to Productfy later// setCryptogram(cryptogram);break;case 'cryptogramFailed':// cryptogram failed to generateconst { message, exception } = event.data;// Message contains user-friendly error message, show client error// setAppError(message);// exception contains detailed information about failure, log// logAppError(exception);break}}return;}, false);
Step 2 - Create Merchant Transaction API
The Create Merchant Transaction API will take the cryptogram generated above by the iFrame. On top of taking the cryptogram and the transaction amount, the billing information is required.
Optional features with this API:
- Enter the destinationAccountId of a virtual account on Productfy for cases where the card payment is being sent to a user's virtual account that is on Productfy. This will automatically create a transaction on the virtual account.
- Enter the requestorPersonId or requestorOrganizatonId to tie the payment to the Person or Organization in the Productfy system
- Enter transactionFees if you would like to add on a surcharge to the credit card payment.
Notes:
- There is an additional version of this API for testing purposes, which would take the credit card information in the clear vs. the cryptogram.
- The ipAddress of the end user is required.
Request parameters
Parameter | Description |
---|---|
ipAddress | the end user IP address |
transactionAmount | the total amount of the merchant transaction, including any fees. |
transactionFees | the transaction fees if any |
transactionDescription | the description of the merchant transaction |
crytpogram | the cryptogram that contains the encrypted card data |
billingName | the name associated to the billing information |
billingStreet | the billing street address |
billingCity | the billing city |
billingState | the billing state. 2-character value is recommended |
billingZip | the billing zip code. 5-character value is recommended |
billingCountry | the billing country. US is the recommended value |
orderId | A unique order id. If not provided by the client, this id will be auto generated. |
requesterPersonId | the Productfy requester person ID. It is an optional field. |
requesterOrganizationId | the Productfy requester organization ID. This is an optional field. |
destinationAccountId | the Productfy destination financial account ID if this transaction's recipient is a Productfy account. |
CreateMerchantTransactionTest API (QA environment only):
This API can be used for testing purposes only and doesn't require the cryptogram from the iFrame above. This may be useful if you just want to test out basic card payment functionality while building your app.
mutation createMerchantTransactionTest {createMerchantTransactionTest(merchantTransactionWithCreditCardRequest: {billingCity: "",billingCountry: "",billingName: "",billingState: "",billingStreet: "",billingZip: "",creditCardCVV: "",creditCardExpirationMonth: "",creditCardExpirationYear: "",creditCardNumber: "",destinationAccountId: "",ipAddress: "",requesterOrganizationId: "",requesterPersonId: "",transactionAmount: 0,transactionDescription: "",transactionFees: 0}) {... on MerchantTransaction {orderIdreferenceNumberauthCodebillingCitybillingCountrybillingNamebillingStatebillingStreetbillingZipdestinationAccountIdlastFourDigitsrefundAmountrefundDaterefundReferenceNumberrefundTyperequesterOrganizationIdrequesterPersonIdtransactionAmounttransactionDatetransactionDescriptiontransactionFeestransactionStatus}... on UserError {__typenameerrors {keymessage}}}}
CreateMerchantTransaction API:
mutation createMerchantTransaction {createMerchantTransaction(merchantTransactionRequest: {billingCity: "",billingCountry: "",billingName: "",billingState: "",billingStreet: "",billingZip: "",crytpogram: "",destinationAccountId: "",ipAddress: "",orderId: "",requesterOrganizationId: "",requesterPersonId: "",transactionAmount: 1.5,transactionDescription: "",transactionFees: 1.5}) {... on MerchantTransaction {orderIdreferenceNumberauthCodebillingCitybillingCountrybillingNamebillingStatebillingStreetbillingZipdestinationAccountIdlastFourDigitsrefundAmountrefundDaterefundReferenceNumberrefundTyperequesterOrganizationIdrequesterPersonIdtransactionAmounttransactionDatetransactionDescriptiontransactionFeestransactionStatus}... on UserError {__typenameerrors {keymessage}}}}
Step 3 - Refund Handling
In order to submit refunds, you will have an available API to call. You will need to pass the reference number associated with the sale transaction and the refund amount. The refundType return parameter can be ‘credit’ or ‘void’, depending if the transaction has already been settled.
Request parameters
Parameter | Description |
---|---|
referenceNumber | the transaction reference number |
transactionAmount | the total amount of the merchant transaction, including any fees. |
Response parameters
Parameter | Description |
---|---|
referenceNumber | the unique reference number for this transaction from the merchant gateway. This is used for requesting a refund. |
transactionStatus | the transaction status should be only Declined or Refund accepted |
refundReferenceNumber | the reference number of the refund (if any) |
refundAmount | the refund amount if the refund was requested |
refundType | the refund type (Void, Refund) |
The GraphQL endpoint is defined as follows:
mutation refundMerchantTransaction {refundMerchantTransaction(referenceNumber: "", transactionAmount: "") {... on UserError {__typenameerrors {keymessage}}... on MerchantRefundTransaction {referenceNumbertransactionStatusrefundAmountrefundReferenceNumberrefundType}}}
Step 4 - Settlement Of Funds
When a sale transaction is successfully submitted, it will be in an APPROVED state. We will receive a batch for the settled transactions daily from the processor. Once we receive the batch we will automatically update the status to SETTLED or VOIDED to the corresponding transactions. The status change will be reflected in through the query API.
Step 5 - Handling Chargebacks
Productfy will trigger a chargeback webhook to you when we are notified that a chargeback has been initiated by one of your customers. Upon receiving this webhook, you will need follow chargeback processes to supply necessary information through the portal. Contact your Productfy representative for more information.
Step 6 - Viewing Your Data
We are offering a query graphQL API endpoint so our client can retrieve in-flight or processed transactions as well as retrieving a transaction by reference number.