Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Gateway provider, APM setup and removing deprecated adyenOverrideMerchantConfig__c field #41

Merged
merged 5 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
run: sf project deploy start --target-org ScratchOrg --ignore-conflicts

- name: Run Apex tests
run: sf apex run test --target-org ScratchOrg --code-coverage -w 5
run: sf apex run test --target-org ScratchOrg --code-coverage --synchronous

- name: Delete Scratch Org
if: always()
Expand Down
2 changes: 1 addition & 1 deletion force-app/main/default/classes/AdyenAsyncAdapter.cls
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ global with sharing class AdyenAsyncAdapter implements CommercePayments.PaymentG
}

public class GatewayException extends Exception {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ public with sharing class AdyenAuthorisationHelper {
String endpoint = adyenAdapterMdt.Endpoint_Api_Version__c + adyenAdapterMdt.Authorize_Endpoint__c;
return AdyenPaymentUtility.makePostRequest(endpoint, body);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>60.0</apiVersion>
<status>Active</status>
</ApexClass>
</ApexClass>
47 changes: 19 additions & 28 deletions force-app/main/default/classes/AdyenCaptureHelper.cls
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,42 @@ public with sharing class AdyenCaptureHelper {
* @return `CommercePayments.GatewayResponse`
*/
public static CommercePayments.GatewayResponse capture(CommercePayments.CaptureRequest captureRequest) {
// Retrieve the PaymentAuthorization
PaymentAuthorization pa = AdyenPaymentUtility.retrievePaymentAuthorization(captureRequest.paymentAuthorizationId);
PaymentAuthorization paymentAuth = AdyenPaymentUtility.retrievePaymentAuthorization(captureRequest.paymentAuthorizationId);

String errorMessage = null;
if(pa == null) {
String errorMessage;
if (paymentAuth == null) {
errorMessage = 'Payment Authorization Missing';
}
if(captureRequest.amount == null) {
errorMessage = 'Payment Amount Missing';
}
String pspReference = AdyenPaymentUtility.getCaptureGatewayRefNumber(pa);
if(String.isBlank(pspReference)) {
} else if (String.isBlank(paymentAuth.GatewayRefNumber)) {
errorMessage = 'PspReference Missing';
} else if (captureRequest.amount == null) {
errorMessage = 'Payment Amount Missing';
}
if(errorMessage != null) {
if (String.isNotBlank(errorMessage)) {
throw new AdyenAsyncAdapter.GatewayException(errorMessage);
}

// By Default, retrieve the metadata key from the order's sales channel
String adapterName = pa.OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c;

// Override config for this specific PaymentAuthorization
if (String.isNotBlank(pa.adyenOverrideMerchantConfig__c)) {
adapterName = pa.adyenOverrideMerchantConfig__c;
}
if (String.isBlank(adapterName)) {
adapterName = AdyenConstants.DEFAULT_ADAPTER_NAME;
}
String pspReference = paymentAuth.GatewayRefNumber;
String merchantAccount = paymentAuth.OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c;
Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.chooseAdapterWithFallBack(merchantAccount);

Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.retrieveGatewayMetadata(adapterName);
CheckoutModificationRequest modRequest = AdyenPaymentUtility.createModificationRequest(captureRequest, pa.CurrencyIsoCode, adyenAdapterMdt);
CheckoutModificationRequest modRequest = createCaptureRequest(captureRequest, paymentAuth, adyenAdapterMdt);
CheckoutCaptureResponse captureResponse = (CheckoutCaptureResponse)AdyenPaymentUtility.sendModificationRequest(modRequest, adyenAdapterMdt, pspReference);
return processCaptureResponse(captureResponse, captureRequest.amount);
}

private static CheckoutCaptureRequest createCaptureRequest(CommercePayments.CaptureRequest captureRequest, PaymentAuthorization paymentAuth, Adyen_Adapter__mdt adyenAdapter) {
CheckoutCaptureRequest modRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(captureRequest, paymentAuth.CurrencyIsoCode, adyenAdapter);
// Line items required for partial captures for Open Invoice methods
if (AdyenPaymentUtility.checkIfOpenInvoiceFromAuthorization(pa)) {
if (AdyenPaymentUtility.checkIfOpenInvoiceFromAuthorization(paymentAuth)) {
String invoiceId = captureRequest.additionalData?.get('invoiceId');
if (String.isNotBlank(invoiceId)) {
modRequest.setLineItems(AdyenPaymentUtility.addInvoiceData(invoiceId));
}
}

CheckoutCaptureResponse captureResponse = (CheckoutCaptureResponse)AdyenPaymentUtility.sendModificationRequest(modRequest, adyenAdapterMdt, pspReference);
return processCaptureResponse(captureResponse, captureRequest.amount);
return modRequest;
}

public static CommercePayments.GatewayResponse processCaptureResponse(CheckoutCaptureResponse captureResponse, Decimal amount) {
private static CommercePayments.GatewayResponse processCaptureResponse(CheckoutCaptureResponse captureResponse, Decimal amount) {
CommercePayments.CaptureResponse salesforceResponse = new CommercePayments.CaptureResponse();
salesforceResponse.setAsync(true);
salesforceResponse.setAmount(Double.valueOf(amount));
Expand Down
12 changes: 8 additions & 4 deletions force-app/main/default/classes/AdyenPaymentHelper.cls
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ public with sharing class AdyenPaymentHelper {
CommercePayments.PaymentGatewayNotificationRequest paymentGatewayNotificationRequest = Test.isRunningTest() ? null : gatewayNotificationContext.getPaymentGatewayNotificationRequest();
NotificationRequestItem notificationRequestItem = parseAdyenNotificationRequest(paymentGatewayNotificationRequest);
Adyen_Adapter__mdt adyenAdapter = AdyenPaymentUtility.retrieveAdapterByMerchantAcct(notificationRequestItem.merchantAccountCode);
HMACValidator validator = new HMACValidator(notificationRequestItem, adyenAdapter.HMAC_Key__c);

if (!Test.isRunningTest() && !validator.validateHMAC()) {
return createAcceptedNotificationResponse('not a valid notification request');
HMACValidator validator;
try {
validator = new HMACValidator(notificationRequestItem, adyenAdapter.HMAC_Key__c);
if (!Test.isRunningTest() && !validator.validateHMAC()) {
return createAcceptedNotificationResponse('not a valid notification request');
}
} catch (HMACValidator.HmacValidationException hmacValidationException) {
return createAcceptedNotificationResponse(hmacValidationException.getMessage());
}

if (!AdyenPaymentUtility.isValidNotification(notificationRequestItem)) {
Expand Down
14 changes: 7 additions & 7 deletions force-app/main/default/classes/AdyenPaymentHelperTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ private class AdyenPaymentHelperTest {
AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody(AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE, null);

Test.startTest();
CommercePayments.GatewayNotificationResponse captureResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
CommercePayments.GatewayNotificationResponse notificationResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
Test.stopTest();

Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
Assert.isFalse(notificationResponse.toString().containsIgnoreCase('error'));
}

@IsTest
static void noRelatedPaymentWebhookTest() {
AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody(AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE, TestDataFactory.GATEWAY_REF);

Test.startTest();
CommercePayments.GatewayNotificationResponse captureResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
CommercePayments.GatewayNotificationResponse notificationResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
Test.stopTest();

Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
Assert.isFalse(notificationResponse.toString().containsIgnoreCase('error'));
}

@IsTest
Expand All @@ -31,10 +31,10 @@ private class AdyenPaymentHelperTest {
AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody(AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE, TestDataFactory.TEST_PSP_REFERENCE);
// when
Test.startTest();
CommercePayments.GatewayNotificationResponse captureResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
CommercePayments.GatewayNotificationResponse notificationResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
Test.stopTest();
// then
Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
Assert.isFalse(notificationResponse.toString().containsIgnoreCase('error'));
}

@IsTest
Expand Down Expand Up @@ -64,4 +64,4 @@ private class AdyenPaymentHelperTest {
Assert.isInstanceOfType(ex, AdyenAsyncAdapter.GatewayException.class);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>60.0</apiVersion>
<status>Active</status>
</ApexClass>
</ApexClass>
74 changes: 24 additions & 50 deletions force-app/main/default/classes/AdyenPaymentUtility.cls
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,6 @@ public with sharing class AdyenPaymentUtility {
private static final String NO_ADYEN_ADAPTER_BY_NAME = 'No Adyen adapter found with this name: ';
@TestVisible
private static final String NO_ADYEN_ADAPTER_BY_MERCHANT = 'No Adyen adapter found for this merchant account: ';

/**
* Looks for the Gateway ref number on the Payment record passed in. If not found gets its from
* the LastPaymentGateway log on the OrderPaymentSummary record.
*
* @param payment the Payment sObject.
* @return the GatewayRefNumber for the request.
*/
public static String getRefundGatewayRefNumber(Payment payment) {
if (payment == null) {
throw new AdyenAsyncAdapter.GatewayException('Payment Info Missing');
}
return payment.PaymentAuthorization?.GatewayRefNumber != null ? payment.PaymentAuthorization.GatewayRefNumber : payment.GatewayRefNumber;
}

/**
* Retrieve Payment Info.
Expand All @@ -33,9 +19,8 @@ public with sharing class AdyenPaymentUtility {
SELECT
Id, GatewayRefNumber, GatewayRefDetails,
PaymentAuthorization.GatewayRefNumber, PaymentAuthorization.Adyen_Payment_Method_Variant__c,
PaymentAuthorization.Adyen_Payment_Method__c, adyenOverrideMerchantConfig__c,
PaymentAuthorization.adyenOverrideMerchantConfig__c,CurrencyIsoCode,
OrderPaymentSummary.FullName, OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c
PaymentAuthorization.Adyen_Payment_Method__c,CurrencyIsoCode, OrderPaymentSummary.FullName,
OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c
FROM
Payment
WHERE
Expand Down Expand Up @@ -83,6 +68,14 @@ public with sharing class AdyenPaymentUtility {
return adyenAdapters[0];
}

public static Adyen_Adapter__mdt chooseAdapterWithFallBack(String merchantAccountName) {
if (String.isNotBlank(merchantAccountName)) {
return retrieveAdapterByMerchantAcct(merchantAccountName);
} else {
return retrieveGatewayMetadata(AdyenConstants.DEFAULT_ADAPTER_NAME);
}
}

public static Boolean isValidNotification(NotificationRequestItem notificationRequestItem) {
return AdyenOMSConstants.VALID_NOTIFICATION_TYPES.contains(notificationRequestItem.eventCode.toUpperCase())
&& isValidPspReference(notificationRequestItem.originalReference)
Expand All @@ -104,7 +97,7 @@ public with sharing class AdyenPaymentUtility {
public static PaymentAuthorization retrievePaymentAuthorization(Id paymentAuthId) {
List<PaymentAuthorization> paymentAuthorizations = [
SELECT
Id, PaymentAuthorizationNumber, GatewayRefNumber, adyenOverrideMerchantConfig__c, Adyen_Payment_Method_Variant__c,
Id, PaymentAuthorizationNumber, GatewayRefNumber, Adyen_Payment_Method_Variant__c,
OrderPaymentSummary.LastPaymentGatewayLog.GatewayRefNumber,
OrderPaymentSummary.Id,
OrderPaymentSummary.FullName, CurrencyIsoCode,
Expand All @@ -130,7 +123,7 @@ public with sharing class AdyenPaymentUtility {
public static Boolean checkIfOpenInvoiceFromAuthorization(PaymentAuthorization pa) {
if (pa != null && pa.Adyen_Payment_Method_Variant__c != null) {
for (String openInvoiceMethod : AdyenOMSConstants.OPEN_INVOICE_METHODS) {
if(pa.Adyen_Payment_Method_Variant__c.containsIgnoreCase(openInvoiceMethod)) {
if (pa.Adyen_Payment_Method_Variant__c.containsIgnoreCase(openInvoiceMethod)) {
return true;
}
}
Expand Down Expand Up @@ -173,20 +166,6 @@ public with sharing class AdyenPaymentUtility {
}
}

/**
* Looks for the Gateway ref number on the PaymentAuthorization record passed in. If not found gets its from
* the LastPaymentGateway log on the OrderPaymentSummary record.
*
* @param pa The PaymentAuthorization sObject
* @return the GatewayRefNumber for the request.
*/
public static String getCaptureGatewayRefNumber(PaymentAuthorization pa) {
if (pa == null) {
throw new AdyenAsyncAdapter.GatewayException('Payment Authorization Missing');
}
return pa.GatewayRefNumber != null ? pa.GatewayRefNumber : pa.OrderPaymentSummary?.LastPaymentGatewayLog?.GatewayRefNumber;
}

public static List<LineItem> addInvoiceData(Id invoiceId) {
List<InvoiceLine> invoiceLines = [
SELECT Id, Product2.Name, Quantity, CurrencyIsoCode, ChargeAmount, ChargeTaxAmount, ChargeAmountWithTax, Type
Expand Down Expand Up @@ -333,38 +312,33 @@ public with sharing class AdyenPaymentUtility {

return info;
}

/**
* Create a modification request by populating required properties (capture/refund)
*
* @param paymentRequest capture or refund request
* @param currencyCode string containing the currency ISO code
* @param adyenAdapter custom metadata type containing the configuration
* @return CheckoutModificationRequest to send to Adyen.
*/
public static CheckoutModificationRequest createModificationRequest(CommercePayments.PaymentGatewayRequest paymentRequest, String currencyCode, Adyen_Adapter__mdt adyenAdapter) {

public static CheckoutModificationRequest createModificationRequest(CommercePayments.PaymentGatewayRequest paymentRequest, String currencyIsoCode, Adyen_Adapter__mdt adyenAdapter) {
CheckoutModificationRequest modRequest;
Decimal price;
String reference;

if (paymentRequest instanceof CommercePayments.CaptureRequest) {
modRequest = new CheckoutCaptureRequest();
CommercePayments.CaptureRequest captureRequest = (CommercePayments.CaptureRequest)paymentRequest;
price = captureRequest.amount;
reference = AdyenPaymentUtility.getReference(captureRequest);
modRequest = new CheckoutCaptureRequest();
reference = getReference(captureRequest);
} else if (paymentRequest instanceof CommercePayments.ReferencedRefundRequest) {
modRequest = new CheckoutRefundRequest();
CommercePayments.ReferencedRefundRequest refundRequest = (CommercePayments.ReferencedRefundRequest)paymentRequest;
modRequest = new CheckoutRefundRequest();
price = refundRequest.amount;
reference = AdyenPaymentUtility.getRandomNumber(16);
reference = getRandomNumber(16);
}

Amount requestAmount = new Amount();
requestAmount.value = (price * AdyenPaymentUtility.getAmountMultiplier(currencyCode)).longValue();
requestAmount.currency_x = currencyCode;
requestAmount.value = (price * getAmountMultiplier(currencyIsoCode)).longValue();
requestAmount.currency_x = currencyIsoCode;

modRequest.setAmount(requestAmount);
modRequest.setReference(reference);
modRequest.setMerchantAccount(adyenAdapter.Merchant_Account__c);
modRequest.setApplicationInfo(AdyenPaymentUtility.getApplicationInfo(adyenAdapter.System_Integrator_Name__c));
modRequest.setApplicationInfo(getApplicationInfo(adyenAdapter.System_Integrator_Name__c));

return modRequest;
}

Expand Down
Loading