1

I have a REST endpoint in Salesforce that accepts the following parameters:

  • Salesforce account ID
  • Billing address
  • Product SKU
  • Currency code
  • Payment method ID

(The customer is expected to create a payment method beforehand using the Zuora hosted payment page, displayed in an iframe on our site.)

Then it does the following:

  1. Creates a new Zuora account with the SF account ID and billing address, or updates if one already exists
  2. Creates a new Zuora subscription for the product SKU and currency code
  3. Creates a new Zuora payment using the invoice amount (created along with the subscription) and payment method ID

In order to create the payment, I'm using the zcreate method provided by Zuora's SOAP API:

Zuora.zObject payment = new Zuora.zObject('Payment');
payment.setValue('AccountId', accountId);
payment.setValue('Amount', invoiceAmount);
payment.setValue('AppliedInvoiceAmount', invoiceAmount);
payment.setValue('EffectiveDate', DateTime.now().format('yyyy-MM-dd\'T\'HH:mm:ss'));
payment.setValue('InvoiceId', invoiceId);
payment.setValue('PaymentMethodId', paymentMethodId);
payment.setValue('Status', 'Processed');
payment.setValue('Type', 'Electronic');

List<Zuora.zApi.SaveResult> saveResults = zuoraApi.zcreate(new List<Zuora.zObject> {
    payment
});

if (saveResults == null || saveResults.isEmpty())
    throw new EndUserMessageException('An error occurred creating the Zuora payment');

Zuora.zApi.SaveResult saveResult = saveResults.get(0);

if (!saveResult.success) {
    string message = '';

    for (Zuora.zObject error : saveResult.errors) {
        if (error.getValue('Field') != null) {
            message += (string)error.getValue('Code') + ' on field ' + error.getValue('Field') + ' - '
                + error.getValue('Message') + '\n';
        } else {
            message += (string)error.getValue('Code') + ' - ' + error.getValue('Message') + '\n';
        }
    }

    throw new EndUserMessageException('An error occurred creating the Zuora payment: ' + message);
}

return saveResult;

If the payment fails (e.g., declined for insufficient funds), I need to delete it, the subscription and the associated invoice from Zuora. The problem is I don't know how to tell if the payment has failed within the REST endpoint in Salesforce. Because I'm using Zuora's API Sandbox environment, the transaction always succeeds. There doesn't seem to be a way to simulate a declined credit card transaction using their SOAP API.

In a production environment, will saveResult.success be false if the payment is declined? And is there any way to test this in the development environment?

1

The saveresult.success will be true if the creation of the payment completes. The Payment Id is in the results.

Use this Id to look up the response from the gateway which will tell you if the payment was declined.

String zoql='SELECT Id,GatewayResponse,GatewayResponseCode from Payment WHERE Id=\''+paymentId+'\' LIMIT 1';

There does not seem to be a good way to test the insufficient funds case. I have been able to temporarily modify my code to pretend there was an error so I could test my code to rollback the payment, invoice and amendment records.

In order to delete the payment from Zuora, you need first to mark the payment voided:

System.debug( '### delete payment id = ' + paymentId);

// Update the payment object in Zuora to Voided
Zuora.zObject paymentVoid = new Zuora.zObject('Payment');
paymentVoid.setValue('Id',paymentId); //an existing account
paymentVoid.setValue('Status','Voided');

List<Zuora.zObject> objs = new List<Zuora.zObject> {paymentVoid};
List<Zuora.zApi.SaveResult> results;

if(!Test.isRunningTest()){
    results = s_zApiInstance.zupdate(objs);
    System.debug( '### update void payment SOAP = ' + Zuora.zApi.currentLogger.toString());
} 

Then you can delete :

// Delete payment object in Zuora
List<String> ids = new List<String>();
ids.add(paymentId);

List<Zuora.zApi.DeleteResult> resultsDelete;

if(!Test.isRunningTest()){
    resultsDelete = s_zApiInstance.zdelete('Payment',ids);
    System.debug( 'delete payment SOAP = ' + Zuora.zApi.currentLogger.toString());
}

Similarly to delete the invoice you need to first update the status to 'Draft', then update the status to 'Canceled', then make the delete call before deleting.

If you also need to delete the Amendments you do so by sorting the list of id's by their created dates such that the last one created is the first one deleted.

  • This is very helpful. When I create the payment, I set the status to "Processed" assuming it will succeed, but then I am unable to delete it (Zuora returns the error: Processed payment cannot be deleted). Do you have to change the status to "Error" before you can delete it? – David Kennedy Nov 16 '15 at 22:09
  • I updated the answer with the steps I take. – Maggie Nov 17 '15 at 2:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.