diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index ab0169a..172fa38 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1 @@
-* @maassenbas @amihajlovski @shubhamvijaivargiya @dcardos
+* @amihajlovski @dcardos @shanikantsingh @shubhamvijaivargiya @zenit2001 @shubhamk67
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index daec67c..978693c 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,10 +1,12 @@
## Summary
-
-**Fixed issue**:
\ No newline at end of file
+
+## Tested scenarios
+Description of tested scenarios:
+
+**Fixed issue**:
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
new file mode 100644
index 0000000..f1e3f69
--- /dev/null
+++ b/.github/workflows/unit-tests.yml
@@ -0,0 +1,60 @@
+name: Salesforce CI/CD
+
+on:
+ pull_request:
+ branches:
+ - '**'
+ workflow_dispatch:
+
+jobs:
+ build-and-deploy:
+ runs-on: ubuntu-latest
+
+ env:
+ PBO_AUTH_URL: ${{ secrets.PBO_AUTH_URL }}
+
+ steps:
+ - name: Checkout This Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: Install Salesforce CLI
+ run: npm install @salesforce/cli --global
+
+ - name: Create authentication file from secret
+ run: echo ${PBO_AUTH_URL} > secret.json
+
+ - name: Authenticate to Dev Hub
+ run: sf org login sfdx-url -f secret.json --set-default-dev-hub
+
+ - name: Create Scratch Org
+ run: sf org create scratch -f config/project-scratch-def.json --set-default --alias ScratchOrg -y 1
+
+ - name: Checkout Apex-Library Repository
+ uses: actions/checkout@v4
+ with:
+ repository: Adyen/adyen-apex-api-library
+ ref: develop
+ path: dependency-repo
+
+ - name: Push Apex Lib Source to Scratch Org
+ run: |
+ cd dependency-repo
+ sf project deploy start --target-org ScratchOrg
+
+ - name: Checkout This Repository Back
+ uses: actions/checkout@v4
+
+ - name: Deploy This Repository Code
+ run: sf project deploy start --target-org ScratchOrg --ignore-conflicts
+
+ - name: Run Apex tests
+ run: sf apex run test --target-org ScratchOrg --code-coverage --synchronous
+
+ - name: Delete Scratch Org
+ if: always()
+ run: sf org delete scratch --target-org ScratchOrg --no-prompt
diff --git a/config/project-scratch-def.json b/config/project-scratch-def.json
index 65b4977..4e42f5d 100644
--- a/config/project-scratch-def.json
+++ b/config/project-scratch-def.json
@@ -1,7 +1,7 @@
{
"orgName": "Adyen_OMS",
"edition": "Developer",
- "features": ["EnableSetPasswordInApi", "OrderManagement", "MultiCurrency"],
+ "features": ["EnableSetPasswordInApi", "OrderManagement", "MultiCurrency", "OrderSaveBehaviorBoth"],
"settings": {
"lightningExperienceSettings": {
"enableS1DesktopEnabled": true
diff --git a/force-app/main/default/classes/AdyenAsyncAdapter.cls b/force-app/main/default/classes/AdyenAsyncAdapter.cls
index 1a3952c..c2c72bd 100644
--- a/force-app/main/default/classes/AdyenAsyncAdapter.cls
+++ b/force-app/main/default/classes/AdyenAsyncAdapter.cls
@@ -1,46 +1,40 @@
/**
- * This adapter is called by the Payment Gateway.
- * The http calls are delegated to the AdyenPaymentHelper Class.
- *
- * This will process a CAPTURE and a REFUND Request as well as the corresponding Async callbacks.
- *
- * @see AdyenPaymentHelper
- * @see AdyenClient
- * @see https://quip.com/z6RVAJzUKYaf
+ * This class is being deprecated after v2 due to the introduction of the payment gateway provider metadata.
+ * This way when upgrading the package conflicts will be avoided.
+ * The new one should be AdyenGatewayAdapter.
*/
global with sharing class AdyenAsyncAdapter implements CommercePayments.PaymentGatewayAdapter,
- CommercePayments.PaymentGatewayAsyncAdapter {
+ CommercePayments.PaymentGatewayAsyncAdapter {
global AdyenAsyncAdapter() {}
- /**
- * The entry point for processing payment requests. Returns the response from the payment gateway.
- * Accepts the gateway context request and handover the operation to AdyenPaymentHelper to call the appropriate capture or refund operation.
- *
- * @param paymentGatewayContext
- * @return CommercePayments.GatewayResponse
- *
- * @implNotes
- * [CAPTURE] is called after setting Fulfillment.Status to 'Fulfilled' which in turns fires processes
- * and flows to create invoices which ultimately fires this.
- *
- * [REFUND] is called by using the action on the order summary page (left hand side).
- *
- */
- global CommercePayments.GatewayResponse processRequest(CommercePayments.paymentGatewayContext paymentGatewayContext) {
+ /**
+ * The entry point for processing payment requests. Returns the response from the payment gateway.
+ * Accepts the gateway context request and handover the operation to AdyenPaymentHelper to call the appropriate capture or refund operation.
+ *
+ * @param paymentGatewayContext from SF
+ * @return CommercePayments.GatewayResponse
+ *
+ * @implNotes
+ * [CAPTURE] is called after setting Fulfillment.Status to 'Fulfilled' which in turns fires processes
+ * and flows to create invoices which ultimately fires this.
+ *
+ * [REFUND] is called by using the action on the order summary page (left hand side).
+ *
+ */
+ global CommercePayments.GatewayResponse processRequest(CommercePayments.PaymentGatewayContext paymentGatewayContext) {
return AdyenPaymentHelper.handleFulfillmentOrderStatusChange(paymentGatewayContext);
}
- /**
- * Listens to the incoming async notification callback from Adyen and handover to AdyenPaymentHelper for processing
- *
- * @param gatewayNotificationContext
- * @return CommercePayments.GatewayNotificationResponse
- */
+ /**
+ * Listens to the incoming async notification callback from Adyen and handover to AdyenPaymentHelper for processing
+ *
+ * @param gatewayNotificationContext from SF
+ * @return CommercePayments.GatewayNotificationResponse
+ */
public CommercePayments.GatewayNotificationResponse processNotification(CommercePayments.PaymentGatewayNotificationContext gatewayNotificationContext) {
- String apexName = String.valueOf(this).substring(0, String.valueOf(this).indexOf(':'));
- return AdyenPaymentHelper.handleAsyncNotificationCallback(gatewayNotificationContext, apexName);
+ return AdyenPaymentHelper.handleAsyncNotificationCallback(gatewayNotificationContext);
}
public class GatewayException extends Exception {}
diff --git a/force-app/main/default/classes/AdyenAsyncAdapter.cls-meta.xml b/force-app/main/default/classes/AdyenAsyncAdapter.cls-meta.xml
index d75b058..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenAsyncAdapter.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenAsyncAdapter.cls-meta.xml
@@ -1,5 +1,5 @@
- 51.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenAsyncAdapterTest.cls b/force-app/main/default/classes/AdyenAsyncAdapterTest.cls
deleted file mode 100644
index 0b01c0b..0000000
--- a/force-app/main/default/classes/AdyenAsyncAdapterTest.cls
+++ /dev/null
@@ -1,152 +0,0 @@
-@isTest
-private class AdyenAsyncAdapterTest {
- @TestSetup
- static void makeData() {
- Account acct = TestDataFactory.createAccount();
- insert acct;
- TestDataFactory.insertBasicPaymentRecords(acct.Id, null);
- }
-
- @IsTest
- static void testCaptureOutboundSuccess() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
-
- Test.startTest();
- Id authId = [SELECT Id FROM PaymentAuthorization ORDER BY CreatedDate DESC LIMIT 1].Id;
- CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(TestDataFactory.TEST_AMOUNT, authId);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
- CommercePayments.GatewayResponse captureResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
-
- Assert.isTrue(captureResponse.toString().contains('[capture-received]'));
- Assert.isTrue(captureResponse.toString().contains(TestDataFactory.TEST_PSP_REFERENCE));
- }
-
- @IsTest
- static void testCaptureOutboundFailure() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
-
- Test.startTest();
- AdyenPaymentUtility.skipMerchantAccount = true;
- Id authId = [SELECT Id FROM PaymentAuthorization ORDER BY CreatedDate DESC LIMIT 1].Id;
- CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(TestDataFactory.TEST_AMOUNT, authId);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
- CommercePayments.GatewayResponse captureResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
-
- Assert.isTrue(captureResponse.toString().contains('SYSTEMERROR'));
- }
-
- @IsTest
- static void testCaptureOutboundMissingPaymentAuthorization() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
- try {
- Test.startTest();
- CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(TestDataFactory.TEST_AMOUNT, null);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
- CommercePayments.GatewayResponse captureResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
- Assert.fail('Exception expected');
- } catch(Exception ex) {
- Assert.areEqual('Payment Authorization Missing', ex.getMessage(), 'Payment Authorization is available');
- }
- }
-
- @IsTest
- static void testCaptureOutboundMissingAmount() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
- Id authId = [SELECT Id FROM PaymentAuthorization ORDER BY CreatedDate DESC LIMIT 1].Id;
- try {
- Test.startTest();
- CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(null, authId);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
- CommercePayments.GatewayResponse captureResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
- Assert.fail('Exception expected');
- } catch(Exception ex) {
- Assert.areEqual('Payment Amount Missing', ex.getMessage(), 'Payment Amount is available.');
- }
- }
-
- @IsTest
- static void testCaptureInboundSuccess() {
- AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody('CAPTURE', TestDataFactory.GATEWAY_REF);
-
- Test.startTest();
- CommercePayments.GatewayNotificationResponse captureResponse = TestDataFactory.adyenAdapter.processNotification(null);
- Test.stopTest();
-
- Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
- }
-
- @IsTest
- static void testRefundInboundSuccess() {
- AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody('REFUND', TestDataFactory.GATEWAY_REF);
-
- Test.startTest();
- CommercePayments.GatewayNotificationResponse captureResponse = TestDataFactory.adyenAdapter.processNotification(null);
- Test.stopTest();
-
- Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
- }
-
- @IsTest
- static void testRefundOutboundSuccess() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
-
- Test.startTest();
- Id paymentId = [SELECT Id FROM Payment ORDER BY CreatedDate DESC LIMIT 1].Id;
- CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(TestDataFactory.TEST_AMOUNT, paymentId);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
- CommercePayments.GatewayResponse refundResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
-
- Assert.isTrue(refundResponse.toString().contains(TestDataFactory.TEST_SHOPPER_REFERENCE));
- }
-
- @IsTest
- static void testRefundOutboundFailure() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
-
- Test.startTest();
- AdyenPaymentUtility.skipMerchantAccount = true;
- Id paymentId = [SELECT Id FROM Payment ORDER BY CreatedDate DESC LIMIT 1].Id;
- CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(TestDataFactory.TEST_AMOUNT, paymentId);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
- CommercePayments.GatewayResponse refundResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
-
- Assert.isTrue(refundResponse.toString().contains('SYSTEMERROR'));
- }
-
- @IsTest
- static void testRefundOutboundMissingPayment() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
- try {
- Test.startTest();
- CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(TestDataFactory.TEST_AMOUNT, null);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
- CommercePayments.GatewayResponse refundResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
- Assert.fail('Exception expected');
- } catch(Exception ex) {
- Assert.areEqual('Payment Info Missing', ex.getMessage(), 'Payment Info is available.');
- }
- }
-
- @IsTest
- static void testRefundOutboundMissingAmount() {
- Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
- Id paymentId = [SELECT Id FROM Payment ORDER BY CreatedDate DESC LIMIT 1].Id;
- try {
- Test.startTest();
- CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(null, paymentId);
- CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
- CommercePayments.GatewayResponse refundResponse = (CommercePayments.GatewayResponse) TestDataFactory.adyenAdapter.processRequest(context);
- Test.stopTest();
- Assert.fail('Exception expected');
- } catch(Exception ex) {
- Assert.areEqual('Payment Amount Missing', ex.getMessage(), 'Payment Amount is available.');
- }
- }
-}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenAuthorisationHelper.cls b/force-app/main/default/classes/AdyenAuthorisationHelper.cls
index de04aae..be05ca5 100644
--- a/force-app/main/default/classes/AdyenAuthorisationHelper.cls
+++ b/force-app/main/default/classes/AdyenAuthorisationHelper.cls
@@ -2,15 +2,14 @@ public with sharing class AdyenAuthorisationHelper {
/**
* Calls Adyen service to post an AUTH request to Adyen.
- * @param authRequest
+ * @param authRequest from CommercePayments
* @return authResponse
*/
public static CommercePayments.GatewayResponse authorise(CommercePayments.AuthorizationRequest authRequest) {
-
Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.retrieveGatewayMetadata(AdyenConstants.DEFAULT_ADAPTER_NAME);
- AuthorisationRequest adyenAuthorisationRequest = AdyenPaymentUtility.createAuthorisationRequest(authRequest, adyenAdapterMdt);
- HttpResponse adyenHttpResponse = AdyenPaymentUtility.sendAuthorisationRequest(adyenAuthorisationRequest, adyenAdapterMdt);
+ AuthorisationRequest adyenAuthorisationRequest = createAuthorisationRequest(authRequest, adyenAdapterMdt);
+ HttpResponse adyenHttpResponse = sendAuthorisationRequest(adyenAuthorisationRequest, adyenAdapterMdt);
return processAuthResponse(adyenHttpResponse, AdyenPaymentUtility.normalizeAmount(authRequest.amount), adyenAdapterMdt.Merchant_Account__c);
}
@@ -19,7 +18,6 @@ public with sharing class AdyenAuthorisationHelper {
Map body = (Map)JSON.deserializeUntyped(response.getBody());
String resultCode = (String)body.get('resultCode');
if(resultCode != null) {
- System.debug('-----> Adyen accepted request');
CommercePayments.AuthorizationResponse salesforceAuthResponse = new CommercePayments.AuthorizationResponse();
if(resultCode == 'Authorised') {
Map additionalData = (Map)body.get('additionalData');
@@ -35,7 +33,6 @@ public with sharing class AdyenAuthorisationHelper {
salesforceAuthResponse.setGatewayReferenceDetails(merchantAccountName);
return salesforceAuthResponse;
} else {
- System.debug('-----> Adyen rejected request');
return new CommercePayments.GatewayErrorResponse(
String.valueOf(response.getStatusCode()),
String.valueOf(body.get('message'))
@@ -43,4 +40,76 @@ public with sharing class AdyenAuthorisationHelper {
}
}
+ /**
+ * Create an AUTH request by populating required properties
+ *
+ * @param authRequest authorization request from SF Commerce Payments
+ * @param adyenAdapterMdt custom metadata used
+ * @return AuthorisationRequest to be sent to Adyen.
+ */
+ public static AuthorisationRequest createAuthorisationRequest(CommercePayments.AuthorizationRequest authRequest, Adyen_Adapter__mdt adyenAdapterMdt) {
+ AuthorisationRequest adyenAuthorisationRequest = new AuthorisationRequest();
+ CommercePayments.AuthApiPaymentMethodRequest paymentMethod = authRequest.paymentMethod;
+ String currencyCode = authRequest.currencyIsoCode.toUpperCase();
+
+ Decimal authAmount = authRequest.amount;
+ adyenAuthorisationRequest.amount = new Amount();
+ adyenAuthorisationRequest.amount.currency_x = currencyCode;
+ adyenAuthorisationRequest.amount.value = (authAmount * AdyenPaymentUtility.getAmountMultiplier(currencyCode)).round(System.RoundingMode.HALF_UP);
+
+ //Use existing token to create auth request
+ if (paymentMethod.id != null) {
+ //paymentMethod.id would be a string that represents the Salesforce record id of CardPaymentMethod or AlternativePaymentMethod object
+ String adyenToken;
+ Id recordId = paymentMethod.id;
+ String sObjName = recordId.getSobjectType().getDescribe().getName(); //determine object name
+
+ if (sObjName == AdyenOMSConstants.CARD_PAYMENT_METHOD_OBJECT) {
+ //for CardPaymentMethod : Use GatewayTokenEncrypted field to retrieve token
+ CardPaymentMethod cpmRecord = [SELECT Id, GatewayTokenEncrypted FROM CardPaymentMethod WHERE Id = :recordId LIMIT 1];
+ adyenToken = cpmRecord.GatewayTokenEncrypted;
+ } else if (sObjName == AdyenOMSConstants.ALTERNATIVE_PAYMENT_METHOD_OBJECT) {
+ //for AlternativePaymentMethod : Use GatewayToken field to retrieve token
+ AlternativePaymentMethod apmRecord = [SELECT Id, GatewayToken FROM AlternativePaymentMethod WHERE Id = :recordId LIMIT 1];
+ adyenToken = apmRecord.GatewayToken;
+ }
+
+ CardDetails cardDetails = new CardDetails();
+ cardDetails.storedPaymentMethodId = adyenToken;
+ adyenAuthorisationRequest.paymentMethod = cardDetails;
+ adyenAuthorisationRequest.shopperInteraction = AuthorisationRequest.ShopperInteractionEnum.ContAuth;
+ adyenAuthorisationRequest.recurringProcessingModel = AuthorisationRequest.RecurringProcessingModelEnum.CardOnFile;
+
+ } else if (paymentMethod.cardPaymentMethod != null) {
+ //use new card details to create auth request
+ CommercePayments.CardPaymentMethodRequest cpmRequest = paymentMethod.cardPaymentMethod;
+ CardDetails cardDetails = new CardDetails();
+ cardDetails.number_x = cpmRequest.cardNumber;
+ cardDetails.expiryMonth = String.valueOf(cpmRequest.expiryMonth);
+ cardDetails.expiryYear = String.valueOf(cpmRequest.expiryYear);
+ cardDetails.holderName = cpmRequest.cardHolderName;
+ cardDetails.cvc = cpmRequest.cvv;
+ adyenAuthorisationRequest.paymentMethod = cardDetails;
+ adyenAuthorisationRequest.shopperInteraction = AuthorisationRequest.ShopperInteractionEnum.Ecommerce;
+ }
+
+ adyenAuthorisationRequest.reference = AdyenPaymentUtility.getRandomNumber(16);
+ adyenAuthorisationRequest.merchantAccount = adyenAdapterMdt.Merchant_Account__c;
+ adyenAuthorisationRequest.shopperReference = UserInfo.getUserId();
+ adyenAuthorisationRequest.applicationInfo = AdyenPaymentUtility.getApplicationInfo(adyenAdapterMdt.System_Integrator_Name__c);
+ return adyenAuthorisationRequest;
+ }
+
+ /**
+ * Send authorisation request to Adyen platform
+ *
+ * @param authRequest to be sent to Adyen
+ * @param adyenAdapterMdt custom metadata used
+ * @return response from adyen platform.
+ */
+ public static HttpResponse sendAuthorisationRequest(AuthorisationRequest authRequest, Adyen_Adapter__mdt adyenAdapterMdt) {
+ String body = AdyenPaymentUtility.makeAdyenCompatible(JSON.serialize(authRequest, true));
+ String endpoint = adyenAdapterMdt.Endpoint_Api_Version__c + adyenAdapterMdt.Authorize_Endpoint__c;
+ return AdyenPaymentUtility.makePostRequest(endpoint, body);
+ }
}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenAuthorisationHelper.cls-meta.xml b/force-app/main/default/classes/AdyenAuthorisationHelper.cls-meta.xml
index 754ecb1..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenAuthorisationHelper.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenAuthorisationHelper.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls b/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls
index 0543b81..90ced69 100644
--- a/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls
+++ b/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls
@@ -1,7 +1,59 @@
-@isTest
+@IsTest
private class AdyenAuthorisationHelperTest {
+ @IsTest
+ static void createAuthorisationRequestTest() {
+ // given
+ Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.retrieveGatewayMetadata(AdyenConstants.DEFAULT_ADAPTER_NAME);
+ AuthorisationRequest adyenAuthRequest;
+ Double price;
+ CommercePayments.AuthorizationRequest authRequest;
+ Long expectedValue;
+ for (Integer i = 0; i < 10; i++) {
+ price = 1008.90 + (0.01 * i);
+ authRequest = TestDataFactory.createAuthorisationRequest(null, price);
+ authRequest.currencyIsoCode = 'USD';
+ // when
+ adyenAuthRequest = AdyenAuthorisationHelper.createAuthorisationRequest(authRequest, adyenAdapterMdt);
+ // then
+ expectedValue = (Decimal.valueOf(price) * 100).longValue();
+ Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
+ }
+ // given a higher amount of decimals
+ for (Integer i = 0; i < 10; i++) {
+ price = 1008.900 + (i * 0.001);
+ authRequest = TestDataFactory.createAuthorisationRequest(null, price);
+ authRequest.currencyIsoCode = 'USD';
+ // when
+ adyenAuthRequest = AdyenAuthorisationHelper.createAuthorisationRequest(authRequest, adyenAdapterMdt);
+ // then
+ expectedValue = (Decimal.valueOf(price) * 100).round(RoundingMode.HALF_UP);
+ Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
+ }
+ // given 0 decimals currency
+ for (Integer i = 0; i < 10; i++) {
+ price = 100890 + i;
+ authRequest = TestDataFactory.createAuthorisationRequest(null, price);
+ authRequest.currencyIsoCode = 'JPY';
+ // when
+ adyenAuthRequest = AdyenAuthorisationHelper.createAuthorisationRequest(authRequest, adyenAdapterMdt);
+ // then
+ expectedValue = Decimal.valueOf(price).longValue();
+ Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
+ }
+ // given 3 decimals currency
+ for (Integer i = 0; i < 10; i++) {
+ price = 100.890 + (i * 0.001);
+ authRequest = TestDataFactory.createAuthorisationRequest(null, price);
+ authRequest.currencyIsoCode = 'JOD';
+ // when
+ adyenAuthRequest = AdyenAuthorisationHelper.createAuthorisationRequest(authRequest, adyenAdapterMdt);
+ // then
+ expectedValue = (Decimal.valueOf(price) * 1000).longValue();
+ Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
+ }
+ }
- @isTest
+ @IsTest
static void authoriseCardPaymentTest() {
CardPaymentMethod cardPayMethodRec = TestDataFactory.createCardPaymentMethod();
insert cardPayMethodRec;
@@ -15,7 +67,7 @@ private class AdyenAuthorisationHelperTest {
Assert.isTrue(gatewayResponse.toString().contains(TestDataFactory.TEST_AUTH_CODE));
}
- @isTest
+ @IsTest
static void authoriseAlternativePaymentTest() {
AlternativePaymentMethod alternativePayMethodRec = TestDataFactory.createAlternativePaymentMethod();
insert alternativePayMethodRec;
@@ -29,14 +81,18 @@ private class AdyenAuthorisationHelperTest {
Assert.isTrue(gatewayResponse.toString().contains(TestDataFactory.TEST_AUTH_CODE));
}
- @isTest
+ @IsTest
static void authorisePaymentFailTest() {
Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
Test.startTest();
- CommercePayments.GatewayResponse gatewayResponse = AdyenAuthorisationHelper.authorise(TestDataFactory.createAuthorisationRequest(null));
+ try {
+ AdyenAuthorisationHelper.authorise(TestDataFactory.createAuthorisationRequest(null));
+ Assert.fail();
+ } catch (Exception ex) {
+ Assert.isInstanceOfType(ex, AdyenGatewayAdapter.GatewayException.class);
+ Assert.isTrue(ex.getMessage().containsIgnoreCase('400'));
+ }
Test.stopTest();
-
- Assert.isFalse(gatewayResponse.toString().contains(TestDataFactory.TEST_AUTH_CODE));
}
}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls-meta.xml b/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls-meta.xml
index 45cccbd..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenAuthorisationHelperTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
-
\ No newline at end of file
+
diff --git a/force-app/main/default/classes/AdyenCaptureHelper.cls b/force-app/main/default/classes/AdyenCaptureHelper.cls
index 928fdd4..73340d2 100644
--- a/force-app/main/default/classes/AdyenCaptureHelper.cls
+++ b/force-app/main/default/classes/AdyenCaptureHelper.cls
@@ -2,91 +2,55 @@ public with sharing class AdyenCaptureHelper {
/**
* invoked by handleFulfillmentOrderStatusChange to capture funds with Adyen
- * @param captureRequest
+ * @param captureRequest with required information
* @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) {
- throw new AdyenAsyncAdapter.GatewayException(errorMessage);
+ if (String.isNotBlank(errorMessage)) {
+ throw new AdyenGatewayAdapter.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);
- String currencyCode = adyenAdapterMdt.Single_Currency_Code__c != null ? adyenAdapterMdt.Single_Currency_Code__c : pa.CurrencyIsoCode;
- CheckoutModificationRequest modRequest = AdyenPaymentUtility.createModificationRequest(
- CommercePayments.RequestType.Capture,
- currencyCode,
- captureRequest.amount,
- adyenAdapterMdt.Merchant_Account__c,
- AdyenPaymentUtility.getReference(pa, captureRequest.amount),
- adyenAdapterMdt.System_Integrator_Name__c
- );
+ 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)) {
- // Get invoice id
- String invoiceId = 'INVALID';
- if (captureRequest.additionalData.containsKey('invoiceId')) {
- invoiceId = captureRequest.additionalData.get('invoiceId');
+ if (AdyenPaymentUtility.checkIfOpenInvoiceFromAuthorization(paymentAuth)) {
+ String invoiceId = captureRequest.additionalData?.get('invoiceId');
+ if (String.isNotBlank(invoiceId)) {
+ modRequest.setLineItems(AdyenPaymentUtility.addInvoiceData(invoiceId));
}
- System.debug('--------> paymentRequest InvoiceId: ' + invoiceId);
- modRequest.setLineItems(AdyenPaymentUtility.addInvoiceData(invoiceId));
}
-
- String captureEndpointURL = adyenAdapterMdt.Capture_Endpoint__c;
- captureEndpointURL = captureEndpointURL.replace('{paymentPspReference}', pspReference);
- HttpResponse adyenHttpResponse = AdyenPaymentUtility.sendModificationRequest(modRequest, adyenAdapterMdt, captureEndpointURL);
- return processCaptureResponse(adyenHttpResponse, captureRequest.amount);
+ return modRequest;
}
- /**
- * @param adyenHttpResponse: Response from Adyen's api after requesting a capture/refund
- * @param amount capture amount
- * @return CommercePayments.GatewayResponse with populated properties.
- */
- public static CommercePayments.GatewayResponse processCaptureResponse(HttpResponse adyenHttpResponse, Decimal amount) {
- CheckoutCaptureResponse adyenResponse = (CheckoutCaptureResponse)JSON.deserialize(AdyenPaymentUtility.makeSalesforceCompatible(adyenHttpResponse.getBody()), CheckoutCaptureResponse.class);
+ private static CommercePayments.GatewayResponse processCaptureResponse(CheckoutCaptureResponse captureResponse, Decimal amount) {
CommercePayments.CaptureResponse salesforceResponse = new CommercePayments.CaptureResponse();
salesforceResponse.setAsync(true);
salesforceResponse.setAmount(Double.valueOf(amount));
salesforceResponse.setGatewayDate(System.now());
- salesforceResponse.setGatewayReferenceDetails(adyenResponse.getReference());
- salesforceResponse.setGatewayResultCode(adyenResponse.getStatus());
-
- if (adyenResponse != null && adyenHttpResponse.getStatusCode() != AdyenConstants.HTTP_ERROR_CODE) { // HTTP connection with Adyen was successful
- salesforceResponse.setGatewayReferenceNumber(adyenResponse.getPSPReference());
- salesforceResponse.setSalesforceResultCodeInfo(AdyenConstants.SUCCESS_SALESFORCE_RESULT_CODE_INFO);
- if (adyenResponse.getStatus() == AdyenConstants.NOTIFICATION_RECEIVED_CHECKOUT) {
- salesforceResponse.setGatewayMessage('[capture-received]');
- }
- } else {
- salesforceResponse.setGatewayReferenceNumber(null);
- salesforceResponse.setSalesforceResultCodeInfo(AdyenConstants.SYSTEM_ERROR_SALESFORCE_RESULT_CODE_INFO);
- }
+ salesforceResponse.setGatewayReferenceDetails(captureResponse.getReference());
+ salesforceResponse.setGatewayResultCode(captureResponse.getStatus());
+ salesforceResponse.setGatewayReferenceNumber(captureResponse.getPSPReference());
+ salesforceResponse.setSalesforceResultCodeInfo(AdyenConstants.SUCCESS_SALESFORCE_RESULT_CODE_INFO);
+ salesforceResponse.setGatewayMessage('[capture-received]');
return salesforceResponse;
}
}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenCaptureHelper.cls-meta.xml b/force-app/main/default/classes/AdyenCaptureHelper.cls-meta.xml
index 754ecb1..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenCaptureHelper.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenCaptureHelper.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenGatewayAdapter.cls b/force-app/main/default/classes/AdyenGatewayAdapter.cls
new file mode 100644
index 0000000..a96b119
--- /dev/null
+++ b/force-app/main/default/classes/AdyenGatewayAdapter.cls
@@ -0,0 +1,18 @@
+global with sharing class AdyenGatewayAdapter implements CommercePayments.PaymentGatewayAdapter, CommercePayments.PaymentGatewayAsyncAdapter {
+
+ global AdyenGatewayAdapter() {}
+
+ global CommercePayments.GatewayResponse processRequest(CommercePayments.PaymentGatewayContext paymentGatewayContext) {
+ try {
+ return AdyenPaymentHelper.handleFulfillmentOrderStatusChange(paymentGatewayContext);
+ } catch (Exception ex) {
+ return new CommercePayments.GatewayErrorResponse('500', ex.getMessage());
+ }
+ }
+
+ global CommercePayments.GatewayNotificationResponse processNotification(CommercePayments.PaymentGatewayNotificationContext gatewayNotificationContext) {
+ return AdyenPaymentHelper.handleAsyncNotificationCallback(gatewayNotificationContext);
+ }
+
+ public class GatewayException extends Exception {}
+}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenAsyncAdapterTest.cls-meta.xml b/force-app/main/default/classes/AdyenGatewayAdapter.cls-meta.xml
similarity index 80%
rename from force-app/main/default/classes/AdyenAsyncAdapterTest.cls-meta.xml
rename to force-app/main/default/classes/AdyenGatewayAdapter.cls-meta.xml
index d75b058..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenAsyncAdapterTest.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenGatewayAdapter.cls-meta.xml
@@ -1,5 +1,5 @@
- 51.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenGatewayAdapterTest.cls b/force-app/main/default/classes/AdyenGatewayAdapterTest.cls
new file mode 100644
index 0000000..952a23e
--- /dev/null
+++ b/force-app/main/default/classes/AdyenGatewayAdapterTest.cls
@@ -0,0 +1,131 @@
+@IsTest
+private class AdyenGatewayAdapterTest {
+ @TestSetup
+ static void makeData() {
+ Account acct = TestDataFactory.createAccount();
+ insert acct;
+ TestDataFactory.insertBasicPaymentRecords(acct.Id, null);
+ }
+
+ @IsTest
+ static void testCaptureOutboundSuccess() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
+
+ Test.startTest();
+ Id authId = [SELECT Id FROM PaymentAuthorization ORDER BY CreatedDate DESC LIMIT 1].Id;
+ CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(TestDataFactory.TEST_AMOUNT, authId);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
+ CommercePayments.GatewayResponse captureResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+
+ Assert.isTrue(captureResponse.toString().contains('[capture-received]'));
+ Assert.isTrue(captureResponse.toString().contains(TestDataFactory.TEST_PSP_REFERENCE));
+ }
+
+ @IsTest
+ static void testCaptureOutboundFailure() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.FailureResponse());
+
+ Test.startTest();
+ Id authId = [SELECT Id FROM PaymentAuthorization ORDER BY CreatedDate DESC LIMIT 1].Id;
+ CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(TestDataFactory.TEST_AMOUNT, authId);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
+ CommercePayments.GatewayResponse gatewayResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+
+ Assert.isInstanceOfType(gatewayResponse, CommercePayments.GatewayErrorResponse.class);
+ Assert.isTrue(gatewayResponse.toString().containsIgnoreCase('400'));
+ }
+
+ @IsTest
+ static void testCaptureOutboundMissingPaymentAuthorization() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
+
+ Test.startTest();
+ CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(TestDataFactory.TEST_AMOUNT, null);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
+ CommercePayments.GatewayResponse gatewayResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+
+ Assert.isInstanceOfType(gatewayResponse, CommercePayments.GatewayErrorResponse.class);
+ Assert.isTrue(gatewayResponse.toString().containsIgnoreCase(AdyenPaymentUtility.NO_PAYMENT_AUTH_FOUND_BY_ID));
+ }
+
+ @IsTest
+ static void testCaptureOutboundMissingAmount() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
+ Id authId = [SELECT Id FROM PaymentAuthorization ORDER BY CreatedDate DESC LIMIT 1].Id;
+ Test.startTest();
+ CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(null, authId);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(captureRequest, CommercePayments.RequestType.Capture);
+ CommercePayments.GatewayResponse gatewayResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+ Assert.isInstanceOfType(gatewayResponse, CommercePayments.GatewayErrorResponse.class);
+ Assert.isTrue(gatewayResponse.toString().containsIgnoreCase('Payment Amount Missing'));
+ }
+
+ @IsTest
+ static void testCaptureInboundSuccess() {
+ AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody('CAPTURE', TestDataFactory.TEST_PSP_REFERENCE);
+
+ Test.startTest();
+ CommercePayments.GatewayNotificationResponse captureResponse = TestDataFactory.adyenAdapter.processNotification(null);
+ Test.stopTest();
+
+ Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
+ }
+
+ @IsTest
+ static void testRefundOutboundSuccess() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
+
+ Test.startTest();
+ Id paymentId = [SELECT Id FROM Payment ORDER BY CreatedDate DESC LIMIT 1].Id;
+ CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(TestDataFactory.TEST_AMOUNT, paymentId);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
+ CommercePayments.GatewayResponse refundResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+
+ Assert.isTrue(refundResponse.toString().contains('received'));
+ }
+
+ @IsTest
+ static void testRefundOutboundFailure() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.FailureResponse());
+
+ Test.startTest();
+ Id paymentId = [SELECT Id FROM Payment ORDER BY CreatedDate DESC LIMIT 1].Id;
+ CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(TestDataFactory.TEST_AMOUNT, paymentId);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
+ CommercePayments.GatewayResponse refundResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+
+ Assert.isInstanceOfType(refundResponse, CommercePayments.GatewayErrorResponse.class);
+ Assert.isTrue(refundResponse.toString().containsIgnoreCase('400'));
+ }
+
+ @IsTest
+ static void testRefundOutboundMissingPayment() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
+ Test.startTest();
+ CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(TestDataFactory.TEST_AMOUNT, null);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
+ CommercePayments.GatewayResponse gatewayResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+ Assert.isInstanceOfType(gatewayResponse, CommercePayments.GatewayErrorResponse.class);
+ Assert.isTrue(gatewayResponse.toString().containsIgnoreCase(AdyenPaymentUtility.NO_PAYMENT_FOUND_BY_ID));
+ }
+
+ @IsTest
+ static void testRefundOutboundMissingAmount() {
+ Test.setMock(HttpCalloutMock.class, new TestDataFactory.EchoHttpMock());
+ Id paymentId = [SELECT Id FROM Payment ORDER BY CreatedDate DESC LIMIT 1].Id;
+ Test.startTest();
+ CommercePayments.ReferencedRefundRequest refundRequest = new CommercePayments.ReferencedRefundRequest(null, paymentId);
+ CommercePayments.PaymentGatewayContext context = new CommercePayments.PaymentGatewayContext(refundRequest, CommercePayments.RequestType.ReferencedRefund);
+ CommercePayments.GatewayResponse gatewayResponse = TestDataFactory.adyenAdapter.processRequest(context);
+ Test.stopTest();
+ Assert.isInstanceOfType(gatewayResponse, CommercePayments.GatewayErrorResponse.class);
+ Assert.isTrue(gatewayResponse.toString().containsIgnoreCase('Payment Amount Missing'));
+ }
+}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenGatewayAdapterTest.cls-meta.xml b/force-app/main/default/classes/AdyenGatewayAdapterTest.cls-meta.xml
new file mode 100644
index 0000000..f5e18fd
--- /dev/null
+++ b/force-app/main/default/classes/AdyenGatewayAdapterTest.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 60.0
+ Active
+
diff --git a/force-app/main/default/classes/AdyenOMSConstants.cls b/force-app/main/default/classes/AdyenOMSConstants.cls
index 4cccb54..0a5928f 100644
--- a/force-app/main/default/classes/AdyenOMSConstants.cls
+++ b/force-app/main/default/classes/AdyenOMSConstants.cls
@@ -6,15 +6,15 @@ public with sharing class AdyenOMSConstants {
'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'
};
- public static final String EXTERNAL_PLATFORM_NAME_FOR_APPINFO = 'Adyen Salesforce OMS';
- public static final String ADYEN_LIBRARY_NAME_FOR_APPINFO = 'adyen-apex-api-library';
- public static final String ADYEN_LIBRARY_VERSION_FOR_APPINFO = '3.0.1';
- public static final String MERCHANT_APP_NAME_FOR_APPINFO = 'adyen-salesforce-oms';
- public static final String MERCHANT_APP_VERSION_FOR_APPINFO = '2.1.2';
+ public static final String EXTERNAL_PLATFORM_NAME_FOR_APP_INFO = 'Adyen Salesforce OMS';
+ public static final String ADYEN_LIBRARY_NAME_FOR_APP_INFO = 'adyen-apex-api-library';
+ public static final String ADYEN_LIBRARY_VERSION_FOR_APP_INFO = '3.2.0';
+ public static final String MERCHANT_APP_NAME_FOR_APP_INFO = 'adyen-salesforce-oms';
+ public static final String MERCHANT_APP_VERSION_FOR_APP_INFO = '3.0.0';
- public static final String CARD_PAYMENTMETHOD_OBJECT = 'CardPaymentMethod';
- public static final String ALTERNATIVE_PAYMENTMETHOD_OBJECT = 'AlternativePaymentMethod';
+ public static final String CARD_PAYMENT_METHOD_OBJECT = 'CardPaymentMethod';
+ public static final String ALTERNATIVE_PAYMENT_METHOD_OBJECT = 'AlternativePaymentMethod';
- public static final String ADYEN_2GP_NAMESPACE = 'adyen_payment';
- public static final Set OPEN_INVOICE_METHODS = new Set {'klarna', 'afterpay', 'ratepay', 'facilypay', 'zip', 'affirm', 'atome', 'walley', 'clearpay'};
+ public static final Set OPEN_INVOICE_METHODS = new Set{'klarna', 'afterpay', 'ratepay', 'facilypay', 'zip', 'affirm', 'atome', 'walley', 'clearpay'};
+ public static final Set VALID_NOTIFICATION_TYPES = new Set{AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE, AdyenConstants.NOTIFICATION_REQUEST_TYPE_REFUND, AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE_FAILED, AdyenConstants.NOTIFICATION_REQUEST_TYPE_REFUND_FAILED};
}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenOMSConstants.cls-meta.xml b/force-app/main/default/classes/AdyenOMSConstants.cls-meta.xml
index fbbad0a..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenOMSConstants.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenOMSConstants.cls-meta.xml
@@ -1,5 +1,5 @@
- 56.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenPaymentHelper.cls b/force-app/main/default/classes/AdyenPaymentHelper.cls
index aa3e27d..92a577a 100644
--- a/force-app/main/default/classes/AdyenPaymentHelper.cls
+++ b/force-app/main/default/classes/AdyenPaymentHelper.cls
@@ -5,14 +5,14 @@ public with sharing class AdyenPaymentHelper {
/**
* Receives Payment Gateway Context from AdyenAsyncAdapter, looks at the context type and then invokes the appropriate Capture or Refund operation
*
- * @param paymentGatewayContext
+ * @param paymentGatewayContext from commerce api
* @return `CommercePayments.GatewayResponse`
*/
public static CommercePayments.GatewayResponse handleFulfillmentOrderStatusChange(CommercePayments.PaymentGatewayContext paymentGatewayContext) {
CommercePayments.RequestType paymentRequestType = paymentGatewayContext.getPaymentRequestType();
CommercePayments.PaymentGatewayRequest paymentRequest = paymentGatewayContext.getPaymentRequest();
- if(paymentRequestType == CommercePayments.RequestType.Authorize){
+ if (paymentRequestType == CommercePayments.RequestType.Authorize) {
return AdyenAuthorisationHelper.authorise((CommercePayments.AuthorizationRequest)paymentRequest);
} else if (paymentRequestType == CommercePayments.RequestType.Capture) {
return AdyenCaptureHelper.capture((CommercePayments.CaptureRequest)paymentRequest);
@@ -24,147 +24,114 @@ public with sharing class AdyenPaymentHelper {
}
}
- public static CommercePayments.GatewayNotificationResponse handleAsyncNotificationCallback(CommercePayments.PaymentGatewayNotificationContext gatewayNotificationContext, String apexName) {
- System.debug('----> Entering AdyenPaymentHelper.handleAsyncNotificationCallback PaymentGatewayNotificationContext= ' + gatewayNotificationContext);
+ public static CommercePayments.GatewayNotificationResponse handleAsyncNotificationCallback(CommercePayments.PaymentGatewayNotificationContext gatewayNotificationContext) {
CommercePayments.PaymentGatewayNotificationRequest paymentGatewayNotificationRequest = Test.isRunningTest() ? null : gatewayNotificationContext.getPaymentGatewayNotificationRequest();
- CommercePayments.GatewayNotificationResponse gatewayNotificationResponse = new CommercePayments.GatewayNotificationResponse();
-
- CommercePayments.NotificationSaveResult notificationSaveResult;
- NotificationRequestItem notificationRequestItem = parseAdyenNotificationRequest( paymentGatewayNotificationRequest );
- String adapterIdFromNotificationData;
- if (notificationRequestItem.originalReference != null) {
- adapterIdFromNotificationData = AdyenPaymentUtility.retrieveApexAdapterId(notificationRequestItem.originalReference);
- }
-
- Id adyenAdapterId = [SELECT Id FROM ApexClass WHERE Name = :apexName AND (NamespacePrefix = :AdyenOMSConstants.ADYEN_2GP_NAMESPACE OR NamespacePrefix = '') LIMIT 1].Id;
-
- if(adapterIdFromNotificationData == adyenAdapterId) {
-
- notificationSaveResult = createNotificationSaveResult( notificationRequestItem );
-
- if (notificationSaveResult != null) {
- if(notificationSaveResult.isSuccess()){ // Notification is accepted by the platform
- gatewayNotificationResponse.setStatusCode(AdyenConstants.HTTP_SUCCESS_CODE);
- gatewayNotificationResponse.setResponseBody(Blob.valueOf(AdyenConstants.NOTIFICATION_ACCEPTED_RESPONSE ));
- System.debug('----> Exiting AdyenPaymentHelper.handleAsyncNotificationCallback after the notification is accepted: ' + gatewayNotificationResponse);
- return gatewayNotificationResponse;
- } else { // Notification is not accepted by the platform, generate system event
- gatewayNotificationResponse.setStatusCode(Integer.valueOf(AdyenConstants.HTTP_SERVER_ERROR_CODE));
- String msg = '[accepted] ';
- if (notificationSaveResult != null && notificationSaveResult.getErrorMessage() != null) {
- msg += notificationSaveResult.getErrorMessage();
- }
- gatewayNotificationResponse.setResponseBody(Blob.valueOf(msg));
- return gatewayNotificationResponse;
- }
- } else {
- String msg = '[accepted] But unsupported notification type: ' + notificationRequestItem.eventCode;
- gatewayNotificationResponse.setResponseBody(Blob.valueOf( msg ));
- gatewayNotificationResponse.setStatusCode(AdyenConstants.HTTP_SUCCESS_CODE);
- return gatewayNotificationResponse;
- }
-
- } else {
- String msg = '[accepted] ';
- if (notificationRequestItem.originalReference == null) {
- msg += 'Notification skipped, original reference is not available';
- gatewayNotificationResponse.setResponseBody(Blob.valueOf(msg));
- gatewayNotificationResponse.setStatusCode(AdyenConstants.HTTP_SUCCESS_CODE);
- System.debug('----> Exiting AdyenPaymentHelper.handleAsyncNotificationCallback, originalReference is n/a: ' + gatewayNotificationResponse);
- } else {
- msg += 'But not processed - wrong payment adapter or wrong instance';
- gatewayNotificationResponse.setResponseBody(Blob.valueOf(msg));
- gatewayNotificationResponse.setStatusCode(AdyenConstants.HTTP_SUCCESS_CODE);
- System.debug('----> Exiting AdyenPaymentHelper.handleAsyncNotificationCallback after identifying that it was the wrong payment adapter: ' + gatewayNotificationResponse);
+ NotificationRequestItem notificationRequestItem = parseAdyenNotificationRequest(paymentGatewayNotificationRequest);
+ Adyen_Adapter__mdt adyenAdapter = AdyenPaymentUtility.retrieveAdapterByMerchantAcct(notificationRequestItem.merchantAccountCode);
+ HMACValidator validator;
+ try {
+ validator = new HMACValidator(notificationRequestItem, adyenAdapter.HMAC_Key__c);
+ if (!Test.isRunningTest() && !validator.validateHMAC()) {
+ return createAcceptedNotificationResponse('not a valid notification request');
}
- return gatewayNotificationResponse;
+ } catch (HMACValidator.HmacValidationException hmacValidationException) {
+ return createAcceptedNotificationResponse(hmacValidationException.getMessage());
}
+
+ if (!AdyenPaymentUtility.isValidNotification(notificationRequestItem)) {
+ return createAcceptedNotificationResponse('no valid psp reference found or webhook type was ignored');
+ }
+
+ if (!AdyenPaymentUtility.relatedPaymentFound(notificationRequestItem.originalReference)) {
+ return createAcceptedNotificationResponse('no related payment record found');
+ }
+
+ createNotificationSaveResult(notificationRequestItem);
+ return createAcceptedNotificationResponse(null);
+ }
+
+ private static CommercePayments.GatewayNotificationResponse createAcceptedNotificationResponse(String reason) {
+ String responseMessage = AdyenConstants.NOTIFICATION_ACCEPTED_RESPONSE;
+ if (!String.isBlank(reason)) {
+ responseMessage += ' But not processed, ' + reason;
+ }
+
+ CommercePayments.GatewayNotificationResponse gatewayNotificationResponse = new CommercePayments.GatewayNotificationResponse();
+ gatewayNotificationResponse.setResponseBody(Blob.valueOf(responseMessage));
+ gatewayNotificationResponse.setStatusCode(AdyenConstants.HTTP_SUCCESS_CODE);
+ return gatewayNotificationResponse;
}
/**
* Take the http request from the async notification callback and deserializes it into AdyenNotificationResponse.
*
- * @param notificationRequest The body of the Adyen notification request.
- * @return AdyenNotificationRequest The deserialized version of the Adyen nodification request.
+ * @param notificationRequest The body of the Adyen notification request.
+ * @return AdyenNotificationRequest The deserialized version of the Adyen notification request.
*
- * @see https://docs.adyen.com/development-resources/webhooks/understand-notifications
*/
public static NotificationRequestItem parseAdyenNotificationRequest(CommercePayments.PaymentGatewayNotificationRequest notificationRequest) {
String adyenNotificationRequestPayload = Test.isRunningTest() ? TEST_NOTIFICATION_REQUEST_BODY : AdyenPaymentUtility.makeSalesforceCompatible(notificationRequest.getRequestBody().toString());
AdyenNotification adyenNotification = (AdyenNotification) JSON.deserialize(adyenNotificationRequestPayload, AdyenNotification.class);
NotificationRequestItem notificationRequestItem = new NotificationRequestItem();
- if(adyenNotification != null) {
- for(NotificationItems notificationItem : adyenNotification.notificationItems) {
- if(notificationItem.NotificationRequestItem != null) {
+ if (adyenNotification != null) {
+ for (NotificationItems notificationItem : adyenNotification.notificationItems) {
+ if (notificationItem.NotificationRequestItem != null) {
notificationRequestItem = notificationItem.NotificationRequestItem;
}
}
}
return notificationRequestItem;
}
-
-
+
/**
- * Creates and records (ie saves) the notificationsaveresult.
+ * Creates and records (ie saves) the notification save result.
*
- * @param notificationRequestItem
+ * @param notificationRequestItem parsed from Adyen web hook
* @return CommercePayments.NotificationSaveResult.
*/
@TestVisible
private static CommercePayments.NotificationSaveResult createNotificationSaveResult( NotificationRequestItem notificationRequestItem ) {
- Boolean unsupportedNotificationType = false;
- CommercePayments.BaseNotification notification = null;
- CommercePayments.NotificationStatus notificationStatus = null;
- CommercePayments.SalesforceResultCode notificationStatusSF = null;
-
- if (!Boolean.valueOf(notificationRequestItem.success)){
- notificationStatus = CommercePayments.NotificationStatus.Failed;
- notificationStatusSF = CommercePayments.SalesforceResultCode.Decline;
- } else {
- notificationStatus = CommercePayments.NotificationStatus.Success;
- notificationStatusSF = CommercePayments.SalesforceResultCode.Success;
- }
-
- if (AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE.equalsIgnoreCase(notificationRequestItem.eventCode)) {
+ CommercePayments.BaseNotification notification;
+ CommercePayments.NotificationStatus notificationStatus;
+ CommercePayments.SalesforceResultCode notificationStatusSF;
+ String gatewayMessageTemplate;
+ Boolean isCaptureRequest = AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE.equalsIgnoreCase(notificationRequestItem.eventCode);
+ Boolean isRefundRequest = AdyenConstants.NOTIFICATION_REQUEST_TYPE_REFUND.equalsIgnoreCase(notificationRequestItem.eventCode);
+ Boolean isCaptureFailedRequest = AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE_FAILED.endsWithIgnoreCase(notificationRequestItem.eventCode);
+ Boolean isRefundFailedRequest = AdyenConstants.NOTIFICATION_REQUEST_TYPE_REFUND_FAILED.endsWithIgnoreCase(notificationRequestItem.eventCode);
+
+ if (isCaptureRequest || isCaptureFailedRequest) {
notification = new CommercePayments.CaptureNotification();
- } else if (AdyenConstants.NOTIFICATION_REQUEST_TYPE_REFUND.equalsIgnoreCase(notificationRequestItem.eventCode)) {
+ gatewayMessageTemplate = '[capture-{0}] {1}';
+ } else if (isRefundRequest || isRefundFailedRequest) {
notification = new CommercePayments.ReferencedRefundNotification();
+ gatewayMessageTemplate = '[refund-{0}] {1}';
} else {
- unsupportedNotificationType = true;
+ throw new AdyenGatewayAdapter.GatewayException('Notification of type ' + notificationRequestItem.eventCode + ' does not match criteria');
}
-
- CommercePayments.NotificationSaveResult notificationSaveResult;
- if(!unsupportedNotificationType) {
- notification.setStatus(notificationStatus);
- notification.setSalesforceResultCodeInfo(new CommercePayments.SalesforceResultCodeInfo(notificationStatusSF));
- notification.setGatewayReferenceNumber(notificationRequestItem.pspReference);
- notification.setGatewayResultCode(notificationRequestItem.eventCode);
- notification.setGatewayResultCodeDescription(notificationRequestItem.reason);
- if(String.isBlank(notificationRequestItem.reason) && notificationStatus == CommercePayments.NotificationStatus.Success) {
- if (AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE.equalsIgnoreCase(notificationRequestItem.eventCode)) {
- notification.setGatewayMessage('[capture-complete]');
- } else if (AdyenConstants.NOTIFICATION_REQUEST_TYPE_REFUND.equalsIgnoreCase(notificationRequestItem.eventCode)) {
- notification.setGatewayMessage('[refund-complete]');
- }
- } else{
- notification.setGatewayMessage(notificationRequestItem.reason);
- }
-
- Decimal value;
- if(notificationRequestItem.amount != null) {
- value = notificationRequestItem.amount.value;
- }
- value = value / AdyenPaymentUtility.getAmountMultiplier(notificationRequestItem.amount.currency_x);
-
- Double dValue = value;
- notification.setAmount(dValue);
- notificationSaveResult = CommercePayments.NotificationClient.record(notification);
- System.debug(JSON.serialize(notificationSaveResult));
- return notificationSaveResult;
+
+ String result;
+ Boolean isSuccessfulNotification = Boolean.valueOf(notificationRequestItem.success);
+ if (isSuccessfulNotification && !isCaptureFailedRequest && !isRefundFailedRequest) {
+ notificationStatus = CommercePayments.NotificationStatus.Success;
+ notificationStatusSF = CommercePayments.SalesforceResultCode.Success;
+ result = 'complete';
} else {
- notificationSaveResult = null;
- System.debug('---> Exiting AdyenPaymentHelper.createNotificationSaveResult after ignoring unsupported notification= ' + notificationRequestItem.eventCode);
- return notificationSaveResult;
+ notificationStatus = CommercePayments.NotificationStatus.Failed;
+ notificationStatusSF = CommercePayments.SalesforceResultCode.Decline;
+ result = 'fail';
}
+ String gatewayMessage = String.format(gatewayMessageTemplate, new List{result, notificationRequestItem.reason});
+ Decimal priceMinorUnits = notificationRequestItem.amount != null ? notificationRequestItem.amount.value : 0;
+ Decimal price = priceMinorUnits / AdyenPaymentUtility.getAmountMultiplier(notificationRequestItem.amount.currency_x);
+
+ notification.setGatewayMessage(gatewayMessage);
+ notification.setStatus(notificationStatus);
+ notification.setSalesforceResultCodeInfo(new CommercePayments.SalesforceResultCodeInfo(notificationStatusSF));
+ notification.setGatewayReferenceNumber(notificationRequestItem.pspReference);
+ notification.setGatewayResultCode(notificationRequestItem.eventCode);
+ notification.setAmount(Double.valueOf(price));
+
+ return CommercePayments.NotificationClient.record(notification);
}
}
\ No newline at end of file
diff --git a/force-app/main/default/classes/AdyenPaymentHelper.cls-meta.xml b/force-app/main/default/classes/AdyenPaymentHelper.cls-meta.xml
index 754ecb1..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenPaymentHelper.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenPaymentHelper.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenPaymentHelperTest.cls b/force-app/main/default/classes/AdyenPaymentHelperTest.cls
index 7911f26..502dfcd 100644
--- a/force-app/main/default/classes/AdyenPaymentHelperTest.cls
+++ b/force-app/main/default/classes/AdyenPaymentHelperTest.cls
@@ -1,35 +1,67 @@
@IsTest
private class AdyenPaymentHelperTest {
@IsTest
- static void handleAsyncNotificationCallbackSkippedTest() {
- AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody('CAPTURE', null);
+ static void notValidWebhookTest() {
+ AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody(AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE, null);
Test.startTest();
- CommercePayments.GatewayNotificationResponse captureResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null, 'AdyenAsyncAdapter');
+ CommercePayments.GatewayNotificationResponse notificationResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
Test.stopTest();
- Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
+ Assert.isFalse(notificationResponse.toString().containsIgnoreCase('error'));
}
@IsTest
- static void handleAsyncNotificationCallbackNotProcessedTest() {
- AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody('CAPTURE', TestDataFactory.GATEWAY_REF);
+ 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, 'AdyenAsyncAdapter');
+ CommercePayments.GatewayNotificationResponse notificationResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
Test.stopTest();
- Assert.isFalse(captureResponse.toString().containsIgnoreCase('error'));
+ Assert.isFalse(notificationResponse.toString().containsIgnoreCase('error'));
}
@IsTest
- static void createNotificationSaveResultTest() {
- NotificationRequestItem nri = TestDataFactory.createNotificationRequestItem(AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE, 'abc123');
+ static void successWebhookTest() {
+ // given notification with related payment
+ Account acct = TestDataFactory.createAccount();
+ insert acct;
+ TestDataFactory.insertBasicPaymentRecords(acct.Id, null);
+ AdyenPaymentHelper.TEST_NOTIFICATION_REQUEST_BODY = TestDataFactory.createNotificationRequestBody(AdyenConstants.NOTIFICATION_REQUEST_TYPE_CAPTURE, TestDataFactory.TEST_PSP_REFERENCE);
+ // when
+ Test.startTest();
+ CommercePayments.GatewayNotificationResponse notificationResponse = AdyenPaymentHelper.handleAsyncNotificationCallback(null);
+ Test.stopTest();
+ // then
+ Assert.isFalse(notificationResponse.toString().containsIgnoreCase('error'));
+ }
+ @IsTest
+ static void createNotificationSaveResultRefundTest() {
+ // given refund fail notification
+ Account acct = TestDataFactory.createAccount();
+ insert acct;
+ TestDataFactory.insertBasicPaymentRecords(acct.Id, null);
+ NotificationRequestItem nri = TestDataFactory.createNotificationRequestItem(AdyenConstants.NOTIFICATION_REQUEST_TYPE_REFUND, TestDataFactory.TEST_PSP_REFERENCE);
+ nri.success = 'false';
+ // when
Test.startTest();
CommercePayments.NotificationSaveResult notificationSaveResult = AdyenPaymentHelper.createNotificationSaveResult(nri);
Test.stopTest();
-
+ // then - no matching payment found
Assert.areEqual(400, notificationSaveResult.getStatusCode());
}
-}
\ No newline at end of file
+
+ @IsTest
+ static void createNotificationExceptionTest() {
+ // given notification with unhandled type
+ NotificationRequestItem nri = TestDataFactory.createNotificationRequestItem(AdyenConstants.NOTIFICATION_REQUEST_TYPE_CANCEL, TestDataFactory.TEST_PSP_REFERENCE);
+ try { // when
+ AdyenPaymentHelper.createNotificationSaveResult(nri);
+ Assert.fail();
+ } catch (Exception ex) { // then
+ Assert.isInstanceOfType(ex, AdyenGatewayAdapter.GatewayException.class);
+ }
+ }
+}
diff --git a/force-app/main/default/classes/AdyenPaymentHelperTest.cls-meta.xml b/force-app/main/default/classes/AdyenPaymentHelperTest.cls-meta.xml
index 45cccbd..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenPaymentHelperTest.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenPaymentHelperTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
-
\ No newline at end of file
+
diff --git a/force-app/main/default/classes/AdyenPaymentUtility.cls b/force-app/main/default/classes/AdyenPaymentUtility.cls
index d492994..6bce282 100644
--- a/force-app/main/default/classes/AdyenPaymentUtility.cls
+++ b/force-app/main/default/classes/AdyenPaymentUtility.cls
@@ -1,23 +1,12 @@
public with sharing class AdyenPaymentUtility {
-
@TestVisible
- private static final String TEST_ENDPOINT = 'https://test.com';
- public static final String MERCHANT_API_KEY = '{!$Credential.Password}';
- public static Boolean skipMerchantAccount = false;
-
- /**
- * 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;
- }
+ private static final String NO_PAYMENT_FOUND_BY_ID = 'No Payment found with this id: ';
+ @TestVisible
+ private static final String NO_PAYMENT_AUTH_FOUND_BY_ID = 'No payment authorization found with this id: ';
+ @TestVisible
+ 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: ';
/**
* Retrieve Payment Info.
@@ -26,85 +15,77 @@ public with sharing class AdyenPaymentUtility {
* @return a Payment sObject.
*/
public static Payment retrievePayment(Id paymentId) {
- Payment payment;
-
- if ( Test.isRunningTest() || (
- Schema.SObjectType.Payment.fields.Id.isAccessible() &&
- Schema.SObjectType.Payment.fields.GatewayRefNumber.isAccessible() &&
- Schema.SObjectType.Payment.fields.GatewayRefDetails.isAccessible() &&
- Schema.SObjectType.Payment.fields.adyenOverrideMerchantConfig__c.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.GatewayRefNumber.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.adyenOverrideMerchantConfig__c.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.Adyen_Payment_Method_Variant__c.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.Adyen_Payment_Method__c.isAccessible() &&
- Schema.SObjectType.Payment.fields.CurrencyIsoCode.isAccessible() &&
- Schema.SObjectType.OrderPaymentSummary.fields.FullName.isAccessible() &&
- Schema.SObjectType.SalesChannel.fields.AdyenMerchantID__c.isAccessible()
- ) ) {
- List payments = [
+ List payments = [
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
Id = :paymentId
- ];
-
- if (!payments.isEmpty()) {
- payment = payments[0];
- }
-
+ ];
+ if (payments.isEmpty()) {
+ throw new AdyenGatewayAdapter.GatewayException(NO_PAYMENT_FOUND_BY_ID + paymentId);
}
- return payment;
+ return payments[0];
}
/**
* Retrieves custom meta data associated with Adyen (Endpoint info) pulls all fields.
- *
+ * @param developerName name of the custom metadata type with Adyen configuration
* @return Adyen_Adapter__mdt for the passed metadata type with all fields.
*/
- public static Adyen_Adapter__mdt retrieveGatewayMetadata(String metaType) {
- if (Test.isRunningTest()) {
- return new Adyen_Adapter__mdt(
- MasterLabel = 'AdyenDefault',
- Single_Currency_Code__c = 'USD',
- System_Integrator_Name__c = 'Test integrator',
- Endpoint_Method__c = 'POST',
- Authorize_Endpoint__c = '/payments',
- Refund_Endpoint__c = '/{paymentPspReference}/refund',
- Capture_Endpoint__c = '/{paymentPspReference}/capture',
- Endpoint_Api_Version__c = '/v1',
- Merchant_Account__c = skipMerchantAccount ? '' : 'TEST_MERCHANT_ACCOUNT'
- );
+ public static Adyen_Adapter__mdt retrieveGatewayMetadata(String developerName) {
+ List adyenAdapters = [
+ SELECT
+ DeveloperName, MasterLabel, Capture_Endpoint__c, Endpoint_Api_Version__c,
+ System_Integrator_Name__c, Endpoint_Path__c, Merchant_Account__c, Refund_Endpoint__c,
+ Authorize_Endpoint__c, HMAC_Key__c
+ FROM Adyen_Adapter__mdt
+ WHERE DeveloperName = :developerName
+ ];
+ if (adyenAdapters.isEmpty()) {
+ throw new AdyenGatewayAdapter.GatewayException(NO_ADYEN_ADAPTER_BY_NAME + developerName);
+ }
+ return adyenAdapters[0];
+ }
+
+ public static Adyen_Adapter__mdt retrieveAdapterByMerchantAcct(String merchantAccountName) {
+ List adyenAdapters = [
+ SELECT
+ DeveloperName, MasterLabel, Capture_Endpoint__c, Endpoint_Api_Version__c,
+ System_Integrator_Name__c, Endpoint_Path__c, Merchant_Account__c, Refund_Endpoint__c,
+ Authorize_Endpoint__c, HMAC_Key__c
+ FROM Adyen_Adapter__mdt
+ WHERE Merchant_Account__c = :merchantAccountName
+ ];
+ if (adyenAdapters.isEmpty()) {
+ throw new AdyenGatewayAdapter.GatewayException(NO_ADYEN_ADAPTER_BY_MERCHANT + merchantAccountName);
+ }
+ return adyenAdapters[0];
+ }
+
+ public static Adyen_Adapter__mdt chooseAdapterWithFallBack(String merchantAccountName) {
+ if (String.isNotBlank(merchantAccountName)) {
+ return retrieveAdapterByMerchantAcct(merchantAccountName);
} else {
- Adyen_Adapter__mdt adyenAdapterMdt;
- if (
- Schema.SObjectType.Adyen_Adapter__mdt.fields.Developername.isAccessible() && Schema.SObjectType.Adyen_Adapter__mdt.fields.NamespacePrefix.isAccessible() &&
- Schema.SObjectType.Adyen_Adapter__mdt.fields.MasterLabel.isAccessible() && Schema.SObjectType.Adyen_Adapter__mdt.fields.Capture_Endpoint__c.isAccessible() &&
- Schema.SObjectType.Adyen_Adapter__mdt.fields.Endpoint_Api_Version__c.isAccessible() && Schema.SObjectType.Adyen_Adapter__mdt.fields.Endpoint_Method__c.isAccessible() &&
- Schema.SObjectType.Adyen_Adapter__mdt.fields.Endpoint_Path__c.isAccessible() && Schema.SObjectType.Adyen_Adapter__mdt.fields.Merchant_Account__c.isAccessible() &&
- Schema.SObjectType.Adyen_Adapter__mdt.fields.System_Integrator_Name__c.isAccessible() && Schema.SObjectType.Adyen_Adapter__mdt.fields.Refund_Endpoint__c.isAccessible() &&
- Schema.SObjectType.Adyen_Adapter__mdt.fields.Single_Currency_Code__c.isAccessible() && Schema.SObjectType.Adyen_Adapter__mdt.fields.Authorize_Endpoint__c.isAccessible()
- ) {
- adyenAdapterMdt = [
- SELECT
- DeveloperName, NamespacePrefix, MasterLabel, Capture_Endpoint__c, Endpoint_Api_Version__c,
- System_Integrator_Name__c, Endpoint_Method__c, Endpoint_Path__c, Merchant_Account__c,
- Refund_Endpoint__c, Single_Currency_Code__c, Authorize_Endpoint__c
- FROM
- Adyen_Adapter__mdt
- WHERE
- DeveloperName = :metaType
- ];
- }
- return adyenAdapterMdt;
+ return retrieveGatewayMetadata(AdyenConstants.DEFAULT_ADAPTER_NAME);
}
}
+
+ public static Boolean isValidNotification(NotificationRequestItem notificationRequestItem) {
+ return AdyenOMSConstants.VALID_NOTIFICATION_TYPES.contains(notificationRequestItem.eventCode.toUpperCase())
+ && isValidPspReference(notificationRequestItem.originalReference)
+ && isValidPspReference(notificationRequestItem.pspReference);
+ }
+
+ // The Adyen PSP Reference number is a alphanumeric value containing 16 characters
+ private static Boolean isValidPspReference(String pspReference) {
+ return String.isNotBlank(pspReference) && pspReference.isAlphanumeric() && pspReference.length() == 16;
+ }
/**
@@ -114,41 +95,23 @@ public with sharing class AdyenPaymentUtility {
* @return a PaymentAuthorization sObject.
*/
public static PaymentAuthorization retrievePaymentAuthorization(Id paymentAuthId) {
- PaymentAuthorization pa;
-
- if ( Test.isRunningTest() || (
- Schema.SObjectType.PaymentAuthorization.fields.Id.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.PaymentAuthorizationNumber.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.GatewayRefNumber.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.CurrencyIsoCode.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.adyenOverrideMerchantConfig__c.isAccessible() &&
- Schema.SObjectType.PaymentAuthorization.fields.Adyen_Payment_Method_Variant__c.isAccessible() &&
- Schema.SObjectType.PaymentGatewayLog.fields.GatewayRefNumber.isAccessible() &&
- Schema.SObjectType.OrderPaymentSummary.fields.Id.isAccessible() &&
- Schema.SObjectType.OrderPaymentSummary.fields.FullName.isAccessible() &&
- Schema.SObjectType.SalesChannel.fields.AdyenMerchantID__c.isAccessible()) )
- {
- List paymentAuthorizations = [
- SELECT
- Id, PaymentAuthorizationNumber, GatewayRefNumber, adyenOverrideMerchantConfig__c, Adyen_Payment_Method_Variant__c,
- OrderPaymentSummary.LastPaymentGatewayLog.GatewayRefNumber,
- OrderPaymentSummary.Id,
- OrderPaymentSummary.FullName, CurrencyIsoCode,
- OrderPaymentSummary.OrderSummary.Id,
- OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c
- FROM
- PaymentAuthorization
- WHERE
- Id = :paymentAuthId
- ORDER BY
- CreatedDate DESC
- ];
- if(!paymentAuthorizations.isEmpty()) {
- pa = paymentAuthorizations[0];
- System.debug(LoggingLevel.INFO, 'SalesChannel Config: ' + pa.OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c);
- }
+ List paymentAuthorizations = [
+ SELECT
+ Id, PaymentAuthorizationNumber, GatewayRefNumber, Adyen_Payment_Method_Variant__c,
+ OrderPaymentSummary.LastPaymentGatewayLog.GatewayRefNumber,
+ OrderPaymentSummary.Id,
+ OrderPaymentSummary.FullName, CurrencyIsoCode,
+ OrderPaymentSummary.OrderSummary.Id,
+ OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c
+ FROM
+ PaymentAuthorization
+ WHERE
+ Id = :paymentAuthId
+ ];
+ if (paymentAuthorizations.isEmpty()) {
+ throw new AdyenGatewayAdapter.GatewayException(NO_PAYMENT_AUTH_FOUND_BY_ID + paymentAuthId);
}
- return pa;
+ return paymentAuthorizations[0];
}
/**
@@ -160,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;
}
}
@@ -171,37 +134,26 @@ public with sharing class AdyenPaymentUtility {
/**
* Retrieve apex adapter id from the gateway reference number.
*
- * @param gatewayRefNumber original payment gatewayrefnumber as recieved in the notification
- * @return apexclass id for the payment gateway adapter.
+ * @param gatewayRefNumber original payment gateway reference number as received in the notification
+ * @return apex class id for the payment gateway adapter.
*/
- public static String retrieveApexAdapterId(String gatewayRefNumber) {
- String apexAdapterId = null;
-
+ public static Boolean relatedPaymentFound(String gatewayRefNumber) {
// Prioritize the payment authorization record if it exists
- for (PaymentAuthorization paymentAuthorization : [
+ List paymentAuthorizations = [
SELECT PaymentGateway.PaymentGatewayProvider.ApexAdapter.Id
FROM PaymentAuthorization
WHERE GatewayRefNumber = :gatewayRefNumber
- ]) {
- if (paymentAuthorization.PaymentGateway.PaymentGatewayProvider.ApexAdapter!=null) {
- apexAdapterId = paymentAuthorization.PaymentGateway.PaymentGatewayProvider.ApexAdapter.Id;
- }
+ ];
+ if (!paymentAuthorizations.isEmpty()) {
+ return true;
}
-
// Fall back to a payment record for pre-captured transactions
- if (apexAdapterId==null) {
- for (Payment payment : [
- SELECT PaymentGateway.PaymentGatewayProvider.ApexAdapter.Id
- FROM Payment
- WHERE GatewayRefNumber = :gatewayRefNumber
- ]) {
- if (payment.PaymentGateway.PaymentGatewayProvider.ApexAdapter!=null) {
- apexAdapterId = payment.PaymentGateway.PaymentGatewayProvider.ApexAdapter.Id;
- }
- }
- }
-
- return apexAdapterId;
+ List payments = [
+ SELECT PaymentGateway.PaymentGatewayProvider.ApexAdapter.Id
+ FROM Payment
+ WHERE GatewayRefNumber = :gatewayRefNumber
+ ];
+ return !payments.isEmpty();
}
public static Integer getAmountMultiplier(String currencyCode) {
@@ -214,21 +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.
- * @see https://ca-test.adyen.com/ca/ca/accounts/showTx.shtml?pspReference=852588546520527A&txType=Payment
- */
- 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 addInvoiceData(Id invoiceId) {
List invoiceLines = [
SELECT Id, Product2.Name, Quantity, CurrencyIsoCode, ChargeAmount, ChargeTaxAmount, ChargeAmountWithTax, Type
@@ -284,11 +221,7 @@ public with sharing class AdyenPaymentUtility {
'number_x' => 'number',
'group_x' => 'group'
};
- String output = input;
- for (String key : mapKeyToReplace.keySet()) {
- output = output.replace(key, mapKeyToReplace.get(key));
- }
- return output;
+ return replaceAttributeName(input, mapKeyToReplace);
}
/**
@@ -299,15 +232,19 @@ public with sharing class AdyenPaymentUtility {
* @return output - the same json string with *_x added back in
*/
public static String makeSalesforceCompatible(String input) {
- String output = input;
Map mapKeyToReplace = new Map{
'recurring.recurringDetailReference' => 'recurring_recurringDetailReference',
'currency' => 'currency_x',
'number' => 'number_x',
'group' => 'group_x'
};
- for (String key : mapKeyToReplace.keySet()) {
- output = output.replace(key, mapKeyToReplace.get(key));
+ return replaceAttributeName(input, mapKeyToReplace);
+ }
+
+ private static String replaceAttributeName(String input, Map fromKeyToValueMap) {
+ String output = input;
+ for (String key : fromKeyToValueMap.keySet()) {
+ output = output.replace(key, fromKeyToValueMap.get(key));
}
return output;
}
@@ -323,45 +260,22 @@ public with sharing class AdyenPaymentUtility {
return String.valueOf(Math.round(Math.random() * MAX)).leftPad(stringLength, '0');
}
- public static FulfillmentOrder getFulfillmentOrder(Id orderSummaryId, Decimal amount) {
- try {
- List fulfillmentOrders = [
- SELECT FulfillmentOrderNumber, Status, StatusCategory, Type, TypeCategory, GrandTotalAmount
- FROM FulfillmentOrder
- WHERE OrderSummaryId = :orderSummaryId
- ORDER BY CreatedDate DESC
- ];
- for (FulfillmentOrder fulfillmentOrder : fulfillmentOrders) {
- if (fulfillmentOrder.GrandTotalAmount == amount) {
- return fulfillmentOrder;
- }
- }
- throw new AdyenAsyncAdapter.GatewayException(
- 'Cannot find any fulfillment order related to order summary Id ' + orderSummaryId + ' with amount ' + amount
- );
- } catch (Exception ex) {
- logException(ex, LoggingLevel.ERROR);
+ public static String getReference(CommercePayments.CaptureRequest captureRequest) {
+ Id invoiceId = captureRequest.additionalData?.get('invoiceId');
+ if (invoiceId == null) {
+ return getRandomNumber(16);
}
- return null;
- }
-
- public static String getReference(SObject anyPaymentTypeRecord, Decimal amount) {
- String randomNumber = getRandomNumber(16);
- OrderPaymentSummary orderPaymentSummary = (OrderPaymentSummary)anyPaymentTypeRecord.getSObject('OrderPaymentSummary');
- if (orderPaymentSummary?.OrderSummaryId == null) {
- return randomNumber;
+ List fulfillmentOrders = [
+ SELECT FulfillmentOrderNumber
+ FROM FulfillmentOrder
+ WHERE InvoiceId = :invoiceId
+ ];
+ if (fulfillmentOrders.isEmpty() || fulfillmentOrders.size() > 1) {
+ return getRandomNumber(16);
}
- String reference = getFulfillmentOrder(orderPaymentSummary.OrderSummaryId, amount)?.FulfillmentOrderNumber;
- return String.isNotBlank(reference) ? reference : randomNumber;
+ return fulfillmentOrders[0].FulfillmentOrderNumber;
}
- public static void logException(Exception ex, LoggingLevel loggingLevel) {
- System.debug(loggingLevel, 'Exception message : ' + ex.getMessage());
- System.debug(loggingLevel, 'Exception type : ' + ex.getTypeName());
- System.debug(loggingLevel, 'Exception line : ' + ex.getLineNumber());
- System.debug(loggingLevel, 'Stacktrace : ' + ex.getStackTraceString());
- }
-
/**
* Round an amount to a normalized value for consistency
*
@@ -375,147 +289,103 @@ public with sharing class AdyenPaymentUtility {
/**
* Add application information to the webservice request
*
+ * @param integratorName as specified in the custom metadata
* @return application information map for the request.
*/
public static ApplicationInfo getApplicationInfo(String integratorName) {
-
ApplicationInfo info = new ApplicationInfo();
ExternalPlatform exPlatform = new ExternalPlatform();
- exPlatform.name = AdyenOMSConstants.EXTERNAL_PLATFORM_NAME_FOR_APPINFO;
+ exPlatform.name = AdyenOMSConstants.EXTERNAL_PLATFORM_NAME_FOR_APP_INFO;
exPlatform.integrator = integratorName;
info.externalPlatform = exPlatform;
CommonField merchantApplication = new CommonField();
- merchantApplication.name = AdyenOMSConstants.MERCHANT_APP_NAME_FOR_APPINFO;
- merchantApplication.version = AdyenOMSConstants.MERCHANT_APP_VERSION_FOR_APPINFO;
+ merchantApplication.name = AdyenOMSConstants.MERCHANT_APP_NAME_FOR_APP_INFO;
+ merchantApplication.version = AdyenOMSConstants.MERCHANT_APP_VERSION_FOR_APP_INFO;
info.merchantApplication = merchantApplication;
CommonField adyenLibrary = new CommonField();
- adyenLibrary.name = AdyenOMSConstants.ADYEN_LIBRARY_NAME_FOR_APPINFO;
- adyenLibrary.version = AdyenOMSConstants.ADYEN_LIBRARY_VERSION_FOR_APPINFO;
+ adyenLibrary.name = AdyenOMSConstants.ADYEN_LIBRARY_NAME_FOR_APP_INFO;
+ adyenLibrary.version = AdyenOMSConstants.ADYEN_LIBRARY_VERSION_FOR_APP_INFO;
info.adyenLibrary = adyenLibrary;
return info;
}
-
- /**
- * Create a modification request by populating required properties (capture/refund)
- *
- * @return CheckoutModificationRequest to send to Adyen.
- */
- public static CheckoutModificationRequest createModificationRequest(CommercePayments.RequestType paymentType, String currencyCode, Decimal amount, String merchantAccount, String reference, String systemIntegratorName) {
+
+ public static CheckoutModificationRequest createModificationRequest(CommercePayments.PaymentGatewayRequest paymentRequest, String currencyIsoCode, Adyen_Adapter__mdt adyenAdapter) {
CheckoutModificationRequest modRequest;
- if (paymentType == CommercePayments.RequestType.Capture) {
+ Decimal price;
+ String reference;
+
+ if (paymentRequest instanceof CommercePayments.CaptureRequest) {
+ CommercePayments.CaptureRequest captureRequest = (CommercePayments.CaptureRequest)paymentRequest;
+ price = captureRequest.amount;
modRequest = new CheckoutCaptureRequest();
- } else if (paymentType == CommercePayments.RequestType.ReferencedRefund) {
+ reference = getReference(captureRequest);
+ } else if (paymentRequest instanceof CommercePayments.ReferencedRefundRequest) {
+ CommercePayments.ReferencedRefundRequest refundRequest = (CommercePayments.ReferencedRefundRequest)paymentRequest;
modRequest = new CheckoutRefundRequest();
+ price = refundRequest.amount;
+ reference = getRandomNumber(16);
}
- modRequest.setReference(reference);
- modRequest.setMerchantAccount(merchantAccount);
+
Amount requestAmount = new Amount();
- requestAmount.value = (amount * AdyenPaymentUtility.getAmountMultiplier(currencyCode)).longValue();
- requestAmount.currency_x = currencyCode;
+ requestAmount.value = (price * getAmountMultiplier(currencyIsoCode)).longValue();
+ requestAmount.currency_x = currencyIsoCode;
+
modRequest.setAmount(requestAmount);
- modRequest.setApplicationInfo(AdyenPaymentUtility.getApplicationInfo(systemIntegratorName));
+ modRequest.setReference(reference);
+ modRequest.setMerchantAccount(adyenAdapter.Merchant_Account__c);
+ modRequest.setApplicationInfo(getApplicationInfo(adyenAdapter.System_Integrator_Name__c));
+
return modRequest;
}
/**
* Send modification request (payment/refund) to Adyen platform
*
+ * @param modRequest request to be sent
+ * @param adyenAdapterMdt custom metadata used
+ * @param pspReference Adyen payment reference
* @return response from adyen platform.
*/
- public static HttpResponse sendModificationRequest(CheckoutModificationRequest modRequest, Adyen_Adapter__mdt adyenAdapterMdt, String endpoint) {
- String body = AdyenPaymentUtility.makeAdyenCompatible(JSON.serialize(modRequest, true));
- String apiKey = MERCHANT_API_KEY;
- endpoint = Test.isRunningTest() ? TEST_ENDPOINT + endpoint : adyenAdapterMdt.Endpoint_Path__c + adyenAdapterMdt.Endpoint_Api_Version__c + endpoint;
- AdyenClient client = new AdyenClient(apiKey, endpoint);
- HttpResponse response = client.request(client.config, body);
- return response;
- }
-
- /**
- * Create an AUTH request by populating required properties
- *
- * @return AuthorisationRequest to send to Adyen.
- */
- public static AuthorisationRequest createAuthorisationRequest(CommercePayments.AuthorizationRequest authRequest, Adyen_Adapter__mdt adyenAdapterMdt){
-
- AuthorisationRequest adyenAuthorisationRequest = new AuthorisationRequest();
- try{
- CommercePayments.AuthApiPaymentMethodRequest paymentMethod = authRequest.paymentMethod;
- String currencyCode = authRequest.currencyIsoCode.toUpperCase();
-
- Decimal authAmount = authRequest.amount;
- adyenAuthorisationRequest.amount = new Amount();
- adyenAuthorisationRequest.amount.currency_x = currencyCode;
- adyenAuthorisationRequest.amount.value = (authAmount * AdyenPaymentUtility.getAmountMultiplier(currencyCode)).round(System.RoundingMode.HALF_UP);
+ public static CheckoutModificationResponse sendModificationRequest(CheckoutModificationRequest modRequest, Adyen_Adapter__mdt adyenAdapterMdt, String pspReference) {
+ String endpoint = adyenAdapterMdt.Endpoint_Api_Version__c;
+ if (modRequest instanceof CheckoutCaptureRequest) {
+ endpoint += adyenAdapterMdt.Capture_Endpoint__c;
+ } else if (modRequest instanceof CheckoutRefundRequest) {
+ endpoint += adyenAdapterMdt.Refund_Endpoint__c;
+ }
+ endpoint = endpoint.replace('{paymentPspReference}', pspReference);
- //Use existing token to create auth request
- if(paymentMethod.id != null){
- //paymentMethod.id would be a string that represents the Salesforce record id of CardPaymentMethod or AlternativePaymentMethod object
- String adyenToken;
- Id recordId = paymentMethod.id;
- String sObjName = recordId.getSobjectType().getDescribe().getName(); //determine object name
-
- if(sObjName == AdyenOMSConstants.CARD_PAYMENTMETHOD_OBJECT) {
- //for CardPaymentMethod : Use GatewayTokenEncrypted field to retrieve token
- CardPaymentMethod cpmRecord = [SELECT Id, GatewayTokenEncrypted FROM CardPaymentMethod WHERE Id = :recordId LIMIT 1];
- adyenToken = cpmRecord.GatewayTokenEncrypted;
- } else if(sObjName == AdyenOMSConstants.ALTERNATIVE_PAYMENTMETHOD_OBJECT) {
- //for AlternativePaymentMethod : Use GatewayToken field to retrieve token
- AlternativePaymentMethod apmRecord = [SELECT Id, GatewayToken FROM AlternativePaymentMethod WHERE Id = :recordId LIMIT 1];
- adyenToken = apmRecord.GatewayToken;
- }
-
- CardDetails cardDetails = new CardDetails();
- cardDetails.storedPaymentMethodId = adyenToken;
- adyenAuthorisationRequest.paymentMethod = cardDetails;
- adyenAuthorisationRequest.shopperInteraction = AuthorisationRequest.ShopperInteractionEnum.ContAuth;
- adyenAuthorisationRequest.recurringProcessingModel = AuthorisationRequest.RecurringProcessingModelEnum.CardOnFile;
-
- } else if(paymentMethod.cardPaymentMethod != null) {
- //use new card details to create auth request
- CommercePayments.CardPaymentMethodRequest cpmRequest = paymentMethod.cardPaymentMethod;
- CardDetails cardDetails = new CardDetails();
- cardDetails.number_x = cpmRequest.cardNumber;
- cardDetails.expiryMonth = String.valueOf(cpmRequest.expiryMonth);
- cardDetails.expiryYear = String.valueOf(cpmRequest.expiryYear);
- cardDetails.holderName = cpmRequest.cardHolderName;
- cardDetails.cvc = cpmRequest.cvv;
- adyenAuthorisationRequest.paymentMethod = cardDetails;
- adyenAuthorisationRequest.shopperInteraction = AuthorisationRequest.ShopperInteractionEnum.Ecommerce;
- }
-
- adyenAuthorisationRequest.reference = AdyenPaymentUtility.getRandomNumber(16);
- adyenAuthorisationRequest.merchantAccount = adyenAdapterMdt.Merchant_Account__c;
- adyenAuthorisationRequest.shopperReference = UserInfo.getUserId();
- adyenAuthorisationRequest.applicationInfo = AdyenPaymentUtility.getApplicationInfo(adyenAdapterMdt.System_Integrator_Name__c);
- } catch(Exception ex) {
- logException(ex, LoggingLevel.ERROR);
+ HttpResponse response = makePostRequest(endpoint, JSON.serialize(modRequest, true));
+
+ CheckoutModificationResponse modificationResponse;
+ String salesforceCompatibleBody = AdyenPaymentUtility.makeSalesforceCompatible(response.getBody());
+ if (modRequest instanceof CheckoutCaptureRequest) {
+ modificationResponse = (CheckoutCaptureResponse)JSON.deserialize(salesforceCompatibleBody, CheckoutCaptureResponse.class);
+ } else if (modRequest instanceof CheckoutRefundRequest) {
+ modificationResponse = (CheckoutRefundResponse)JSON.deserialize(salesforceCompatibleBody, CheckoutRefundResponse.class);
}
- return adyenAuthorisationRequest;
+ return modificationResponse;
}
-
- /**
- * Send authorisation request to Adyen platform
- *
- * @return response from adyen platform.
- */
- public static HttpResponse sendAuthorisationRequest(AuthorisationRequest authRequest, Adyen_Adapter__mdt adyenAdapterMdt){
- HttpResponse response;
- String endpoint;
- try{
- String body = AdyenPaymentUtility.makeAdyenCompatible(JSON.serialize(authRequest, true));
- String apiKey = MERCHANT_API_KEY;
- endpoint = Test.isRunningTest() ? TEST_ENDPOINT + adyenAdapterMdt.Authorize_Endpoint__c : adyenAdapterMdt.Endpoint_Path__c + adyenAdapterMdt.Endpoint_Api_Version__c + adyenAdapterMdt.Authorize_Endpoint__c;
- AdyenClient client = new AdyenClient(apiKey, endpoint);
- response = client.request(client.config, body);
- } catch(Exception ex) {
- logException(ex, LoggingLevel.ERROR);
+
+ public static HttpResponse makePostRequest(String endpoint, String body) {
+ String compatibleBody = makeAdyenCompatible(body);
+
+ HttpRequest request = new HttpRequest();
+ request.setEndpoint(endpoint);
+ request.setMethod('POST');
+ request.setHeader('Content-Type', 'application/json');
+ request.setBody(compatibleBody);
+
+ CommercePayments.PaymentsHttp paymentsHttp = new CommercePayments.PaymentsHttp();
+ HttpResponse response = paymentsHttp.send(request);
+ if (response.getStatusCode() != 200 && response.getStatusCode() != 201) {
+ throw new AdyenGatewayAdapter.GatewayException('Adyen Checkout API returned: ' + response.getStatusCode() + ', body: ' + response.getBody());
+ } else {
+ return response;
}
- return response;
}
-
-}
\ No newline at end of file
+}
diff --git a/force-app/main/default/classes/AdyenPaymentUtility.cls-meta.xml b/force-app/main/default/classes/AdyenPaymentUtility.cls-meta.xml
index 754ecb1..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenPaymentUtility.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenPaymentUtility.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenPaymentUtilityTest.cls b/force-app/main/default/classes/AdyenPaymentUtilityTest.cls
index 7688de1..c377bb0 100644
--- a/force-app/main/default/classes/AdyenPaymentUtilityTest.cls
+++ b/force-app/main/default/classes/AdyenPaymentUtilityTest.cls
@@ -1,113 +1,60 @@
@IsTest
private class AdyenPaymentUtilityTest {
- private static String ASSERT_PRICE_MESSAGE = 'For input price of ';
- @IsTest
- static void createAuthorisationRequestTest() {
- // given
- Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.retrieveGatewayMetadata(AdyenConstants.DEFAULT_ADAPTER_NAME);
- AuthorisationRequest adyenAuthRequest;
- Double price;
- CommercePayments.AuthorizationRequest authRequest;
- Long expectedValue;
- for (Integer i = 0; i < 10; i++) {
- price = 1008.90 + (0.01 * i);
- authRequest = new CommercePayments.AuthorizationRequest(price);
- authRequest.currencyIsoCode = 'USD';
- // when
- adyenAuthRequest = AdyenPaymentUtility.createAuthorisationRequest(authRequest, adyenAdapterMdt);
- // then
- expectedValue = (Decimal.valueOf(price) * 100).longValue();
- Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
- }
- // given a higher amount of decimals
- for (Integer i = 0; i < 10; i++) {
- price = 1008.900 + (i * 0.001);
- authRequest = new CommercePayments.AuthorizationRequest(price);
- authRequest.currencyIsoCode = 'USD';
- // when
- adyenAuthRequest = AdyenPaymentUtility.createAuthorisationRequest(authRequest, adyenAdapterMdt);
- // then
- expectedValue = (Decimal.valueOf(price) * 100).round(RoundingMode.HALF_UP);
- Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
- }
- // given 0 decimals currency
- for (Integer i = 0; i < 10; i++) {
- price = 100890 + i;
- authRequest = new CommercePayments.AuthorizationRequest(price);
- authRequest.currencyIsoCode = 'JPY';
- // when
- adyenAuthRequest = AdyenPaymentUtility.createAuthorisationRequest(authRequest, adyenAdapterMdt);
- // then
- expectedValue = Decimal.valueOf(price).longValue();
- Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
- }
- // given 3 decimals currency
- for (Integer i = 0; i < 10; i++) {
- price = 100.890 + (i * 0.001);
- authRequest = new CommercePayments.AuthorizationRequest(price);
- authRequest.currencyIsoCode = 'JOD';
- // when
- adyenAuthRequest = AdyenPaymentUtility.createAuthorisationRequest(authRequest, adyenAdapterMdt);
- // then
- expectedValue = (Decimal.valueOf(price) * 1000).longValue();
- Assert.areEqual(expectedValue, adyenAuthRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
- }
- }
-
@IsTest
private static void createModificationRequestTest() {
//given
- CommercePayments.RequestType paymentType = CommercePayments.RequestType.Capture;
+ CommercePayments.CaptureRequest captureRequest;
String currencyCode = 'USD';
Double price; // request comes as Double value
- String merchantAccount = 'test_merchant';
- String reference = 'test_reference';
- String systemIntegratorName = 'test_integrator';
+ Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.retrieveGatewayMetadata(AdyenConstants.DEFAULT_ADAPTER_NAME);
Decimal expectedPrice;
CheckoutCaptureRequest modificationRequest;
for (Integer i = 0; i < 10; i++) {
price = 100.90 + (0.01 * i);
+ captureRequest = new CommercePayments.CaptureRequest(price, null);
// when
- modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(paymentType, currencyCode, price, merchantAccount, reference, systemIntegratorName);
+ modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(captureRequest, currencyCode, adyenAdapterMdt);
// then
expectedPrice = 100 * Decimal.valueOf(price);
- Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
+ Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
}
// given 3 decimals currency
currencyCode = 'JOD';
for (Integer i = 0; i < 10; i++) {
price = 100.990 + (0.001 * i);
+ captureRequest = new CommercePayments.CaptureRequest(price, null);
// when
- modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(paymentType, currencyCode, price, merchantAccount, reference, systemIntegratorName);
+ modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(captureRequest, currencyCode, adyenAdapterMdt);
// then
expectedPrice = 1000 * Decimal.valueOf(price);
- Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
+ Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
}
// given no decimals currency
currencyCode = 'JPY';
for (Integer i = 0; i < 10; i++) {
price = 100990 + i;
+ captureRequest = new CommercePayments.CaptureRequest(price, null);
// when
- modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(paymentType, currencyCode, price, merchantAccount, reference, systemIntegratorName);
+ modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(captureRequest, currencyCode, adyenAdapterMdt);
// then
expectedPrice = Decimal.valueOf(price);
- Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
+ Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
}
// given more decimals than necessary
currencyCode = 'EUR';
for (Integer i = 0; i < 10; i++) {
price = 10.990 + (0.001 * i);
+ captureRequest = new CommercePayments.CaptureRequest(price, null);
// when
- modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(paymentType, currencyCode, price, merchantAccount, reference, systemIntegratorName);
+ modificationRequest = (CheckoutCaptureRequest)AdyenPaymentUtility.createModificationRequest(captureRequest, currencyCode, adyenAdapterMdt);
// then
expectedPrice = 100 * Decimal.valueOf(price);
- Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, ASSERT_PRICE_MESSAGE + price);
+ Assert.areEqual(expectedPrice.longValue(), modificationRequest.amount.value, TestDataFactory.ASSERT_PRICE_MESSAGE + price);
}
// checking other properties
Assert.areEqual(currencyCode, modificationRequest.amount.currency_x);
- Assert.areEqual(reference, modificationRequest.getReference());
- Assert.areEqual(merchantAccount, modificationRequest.getMerchantAccount());
- Assert.areEqual(systemIntegratorName, modificationRequest.getApplicationInfo().externalPlatform.integrator);
+ Assert.areEqual(adyenAdapterMdt.Merchant_Account__c, modificationRequest.getMerchantAccount());
+ Assert.areEqual(adyenAdapterMdt.System_Integrator_Name__c, modificationRequest.getApplicationInfo().externalPlatform.integrator);
}
@IsTest(SeeAllData=true) // for ConnectApi use only
@@ -128,6 +75,7 @@ private class AdyenPaymentUtilityTest {
Assert.areEqual(productPrice*100, lineItems[0].amountExcludingTax);
Assert.areEqual((productPrice+taxValue)*100, lineItems[0].amountIncludingTax);
}
+
@IsTest(SeeAllData=true) // for ConnectApi use only
static void addCreditMemoDataTest() {
Integer productPrice = 100;
@@ -161,65 +109,49 @@ private class AdyenPaymentUtilityTest {
Assert.areEqual(0, lineItems[0].taxPercentage);
}
- @IsTest(SeeAllData=true) // for ConnectApi use only
- static void getFulfillmentOrderTest() {
- OrderPaymentSummary orderPaySum = createInvoiceAndRelatedRecords(100, 5);
- Test.startTest();
- FulfillmentOrder fulfillmentOrder = AdyenPaymentUtility.getFulfillmentOrder(orderPaySum.OrderSummaryId, 105);
- Test.stopTest();
- Assert.isNotNull(fulfillmentOrder);
- }
-
- @IsTest
- static void getFulfillmentOrderFailTest() {
- Test.startTest();
- FulfillmentOrder fulfillmentOrder = AdyenPaymentUtility.getFulfillmentOrder(null, 0);
- Test.stopTest();
- Assert.isNull(fulfillmentOrder);
- }
-
@IsTest(SeeAllData=true) // for ConnectApi use only
static void getReferenceTest() {
+ // given
OrderPaymentSummary orderPaySum = createInvoiceAndRelatedRecords(100, 5);
PaymentAuthorization payAuth = [
- SELECT OrderPaymentSummary.OrderSummaryId
+ SELECT Id
FROM PaymentAuthorization
WHERE OrderPaymentSummaryId = :orderPaySum.Id
];
- Payment payment = [
- SELECT OrderPaymentSummary.OrderSummaryId
- FROM Payment
- WHERE OrderPaymentSummaryId = :orderPaySum.Id
- ];
FulfillmentOrder fulfillmentOrder = [
- SELECT FulfillmentOrderNumber
+ SELECT FulfillmentOrderNumber, InvoiceId, GrandTotalAmount
FROM FulfillmentOrder
WHERE OrderSummaryId = :orderPaySum.OrderSummaryId
];
-
+ CommercePayments.CaptureRequest captureRequest = new CommercePayments.CaptureRequest(Double.valueOf(fulfillmentOrder.GrandTotalAmount), payAuth.Id);
+ captureRequest.additionalData = new Map();
+ captureRequest.additionalData.put('invoiceId', fulfillmentOrder.InvoiceId);
+ // when
Test.startTest();
- String referencePayAuth = AdyenPaymentUtility.getReference(payAuth, 105);
- String referencePayment = AdyenPaymentUtility.getReference(payment, 105);
+ String referencePayAuth = AdyenPaymentUtility.getReference(captureRequest);
Test.stopTest();
-
+ // then
Assert.areEqual(fulfillmentOrder.FulfillmentOrderNumber, referencePayAuth);
- Assert.areEqual(fulfillmentOrder.FulfillmentOrderNumber, referencePayment);
}
@IsTest
static void getReferenceRandomTest() {
- OrderPaymentSummary orderPaymentSummary = new OrderPaymentSummary(Id = '0bM7Q000000QP0uUAG', OrderSummaryId = '1Os7Q000000Uh77SAC');
- PaymentAuthorization payAuth = new PaymentAuthorization(OrderPaymentSummary = orderPaymentSummary);
-
+ // given
+ CommercePayments.CaptureRequest captureRequest1 = new CommercePayments.CaptureRequest(Double.valueOf(99.9), '0Xc7Q000000YFVcSAO');
+ CommercePayments.CaptureRequest captureRequest2 = new CommercePayments.CaptureRequest(Double.valueOf(99.9), '0Xc7Q000000YFVcSAO');
+ captureRequest2.additionalData = new Map();
+ captureRequest2.additionalData.put('invoiceId', '3tt7Q000000cJbuQAE');
+ // when
Test.startTest();
- String reference = AdyenPaymentUtility.getReference(payAuth, 23);
- String referenceNullOrderPaySum = AdyenPaymentUtility.getReference(new PaymentAuthorization(), 23);
+ String reference1 = AdyenPaymentUtility.getReference(captureRequest1);
+ String reference2 = AdyenPaymentUtility.getReference(captureRequest2);
Test.stopTest();
-
- Assert.isNotNull(reference);
- Assert.isNotNull(referenceNullOrderPaySum);
- Assert.areEqual(16, reference.length());
- Assert.areEqual(16, referenceNullOrderPaySum.length());
+ // then
+ Assert.isNotNull(reference1);
+ Assert.isNotNull(reference2);
+ Assert.areEqual(16, reference1.length());
+ Assert.areEqual(16, reference2.length());
+ Assert.areNotEqual(reference1, reference2);
}
private static OrderPaymentSummary createInvoiceAndRelatedRecords(Decimal price, Decimal taxValue) {
diff --git a/force-app/main/default/classes/AdyenPaymentUtilityTest.cls-meta.xml b/force-app/main/default/classes/AdyenPaymentUtilityTest.cls-meta.xml
index 754ecb1..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenPaymentUtilityTest.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenPaymentUtilityTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/AdyenRefundHelper.cls b/force-app/main/default/classes/AdyenRefundHelper.cls
index 7ded013..6deccd5 100644
--- a/force-app/main/default/classes/AdyenRefundHelper.cls
+++ b/force-app/main/default/classes/AdyenRefundHelper.cls
@@ -8,49 +8,37 @@ public with sharing class AdyenRefundHelper {
* @param refundRequest The CommercePayments.ReferencedRefundRequest Object.
* @return refundResponse The CommercePayments.ReferencedRefundResponse Object.
*
- * @see AdyenClient
*/
public static CommercePayments.GatewayResponse refund(CommercePayments.ReferencedRefundRequest refundRequest) {
- // Retrieve the Payment
Payment payment = AdyenPaymentUtility.retrievePayment(refundRequest.paymentId);
String errorMessage = null;
- if(payment == null) {
+ if (payment == null) {
errorMessage = 'Payment Info Missing';
- }
- if(refundRequest.amount == null) {
+ } else if (String.isBlank(payment.GatewayRefNumber)) {
+ errorMessage = 'PspReference Missing';
+ } else if (refundRequest.amount == null) {
errorMessage = 'Payment Amount Missing';
}
- if(errorMessage != null) {
- throw new AdyenAsyncAdapter.GatewayException(errorMessage);
- }
- // By Default, retrieve the metadata key from the order's sales channel
- String adapterName = payment?.OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c;
- // Override config for this specific Payment (i.e., a pre-capture) or inherit override from the original PaymentAuthorization
- if (String.isNotBlank(payment?.adyenOverrideMerchantConfig__c)) {
- adapterName = payment.adyenOverrideMerchantConfig__c;
- }
- if (String.isNotBlank(payment?.PaymentAuthorization?.adyenOverrideMerchantConfig__c)) {
- adapterName = payment.PaymentAuthorization.adyenOverrideMerchantConfig__c;
+ if (errorMessage != null) {
+ throw new AdyenGatewayAdapter.GatewayException(errorMessage);
}
- if (String.isBlank(adapterName)) {
- adapterName = AdyenConstants.DEFAULT_ADAPTER_NAME;
- }
- Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.retrieveGatewayMetadata(adapterName);
- String currencyCode = adyenAdapterMdt.Single_Currency_Code__c != null ? adyenAdapterMdt.Single_Currency_Code__c : payment.CurrencyIsoCode.toUpperCase();
- String pspReference = (payment.OrderPaymentSummary.FullName == 'DeclineRefund' ? 'dummytransaction' : AdyenPaymentUtility.getRefundGatewayRefNumber(payment));
- CheckoutModificationRequest modRequest = AdyenPaymentUtility.createModificationRequest(
- CommercePayments.RequestType.ReferencedRefund,
- currencyCode, refundRequest.amount,
- adyenAdapterMdt.Merchant_Account__c,
- AdyenPaymentUtility.getRandomNumber(16),
- adyenAdapterMdt.System_Integrator_Name__c
- );
+
+ String pspReference = payment.PaymentAuthorization?.GatewayRefNumber != null ? payment.PaymentAuthorization.GatewayRefNumber : payment.GatewayRefNumber;
+ String merchantAccount = payment.OrderPaymentSummary.OrderSummary.SalesChannel.AdyenMerchantID__c;
+ Adyen_Adapter__mdt adyenAdapterMdt = AdyenPaymentUtility.chooseAdapterWithFallBack(merchantAccount);
+
+ CheckoutRefundRequest modRequest = createRefundRequest(refundRequest, payment, adyenAdapterMdt);
+ CheckoutRefundResponse refundResponse = (CheckoutRefundResponse)AdyenPaymentUtility.sendModificationRequest(modRequest, adyenAdapterMdt, pspReference);
+ return processRefundResponse(refundResponse, refundRequest.amount);
+ }
+
+ private static CheckoutRefundRequest createRefundRequest(CommercePayments.ReferencedRefundRequest refundRequest, Payment payment, Adyen_Adapter__mdt adyenAdapter) {
+ CheckoutRefundRequest modRequest = (CheckoutRefundRequest)AdyenPaymentUtility.createModificationRequest(refundRequest, payment.CurrencyIsoCode, adyenAdapter);
//Only for Paypal Refunds - Capture reference must be a substring of refund reference
if (String.isNotBlank(payment.PaymentAuthorization.Adyen_Payment_Method_Variant__c)) {
if (payment.PaymentAuthorization.Adyen_Payment_Method_Variant__c.equalsIgnoreCase('Paypal') && String.isNotBlank(payment.GatewayRefDetails)) {
- String refundReference = modRequest.getReference() + payment.GatewayRefDetails; //payment.GatewayRefDetails has the capture reference
- System.debug('refundReference for Paypal :: ' + refundReference);
+ String refundReference = modRequest.getReference() + payment.GatewayRefDetails;
modRequest.setReference(refundReference);
}
}
@@ -59,36 +47,19 @@ public with sharing class AdyenRefundHelper {
modRequest.setLineItems(AdyenPaymentUtility.addCreditMemoData(payment.OrderPaymentSummary.OrderSummaryId));
}
- String refundEndpointURL = adyenAdapterMdt.Refund_Endpoint__c;
- refundEndpointURL = refundEndpointURL.replace('{paymentPspReference}', pspReference);
- HttpResponse adyenHttpResponse = AdyenPaymentUtility.sendModificationRequest(modRequest, adyenAdapterMdt, refundEndpointURL);
- return processRefundResponse(adyenHttpResponse, refundRequest.amount);
+ return modRequest;
}
- /**
- * @param adyenHttpResponse: Response from Adyen's api after requesting a refund
- * @param amount to be refunded
- * @return CommercePayments.GatewayResponse with populated properties.
- */
- public static CommercePayments.GatewayResponse processRefundResponse(HttpResponse adyenHttpResponse, Decimal amount) {
+ private static CommercePayments.GatewayResponse processRefundResponse(CheckoutRefundResponse refundResponse, Decimal amount) {
CommercePayments.ReferencedRefundResponse salesforceResponse = new CommercePayments.ReferencedRefundResponse();
- CheckoutRefundResponse adyenResponse = (CheckoutRefundResponse)JSON.deserialize(AdyenPaymentUtility.makeSalesforceCompatible(adyenHttpResponse.getBody()), CheckoutRefundResponse.class);
salesforceResponse.setAsync(true);
salesforceResponse.setAmount(Double.valueOf(amount));
salesforceResponse.setGatewayDate(System.now());
- salesforceResponse.setGatewayReferenceDetails(adyenResponse.getReference());
- salesforceResponse.setGatewayResultCode(adyenResponse.getStatus());
-
- if (adyenResponse != null && adyenHttpResponse.getStatusCode() != AdyenConstants.HTTP_ERROR_CODE) { // HTTP connection with Adyen was successful
- salesforceResponse.setGatewayReferenceNumber(adyenResponse.getPSPReference());
- salesforceResponse.setSalesforceResultCodeInfo(AdyenConstants.SUCCESS_SALESFORCE_RESULT_CODE_INFO);
- if (adyenResponse.getStatus() == AdyenConstants.NOTIFICATION_RECEIVED_CHECKOUT) {
- salesforceResponse.setGatewayMessage('[refund-received]');
- }
- } else {
- salesforceResponse.setGatewayReferenceNumber(null);
- salesforceResponse.setSalesforceResultCodeInfo(AdyenConstants.SYSTEM_ERROR_SALESFORCE_RESULT_CODE_INFO);
- }
+ salesforceResponse.setGatewayReferenceDetails(refundResponse.getReference());
+ salesforceResponse.setGatewayResultCode(refundResponse.getStatus());
+ salesforceResponse.setGatewayReferenceNumber(refundResponse.getPSPReference());
+ salesforceResponse.setSalesforceResultCodeInfo(AdyenConstants.SUCCESS_SALESFORCE_RESULT_CODE_INFO);
+ salesforceResponse.setGatewayMessage('[refund-received]');
return salesforceResponse;
}
-}
\ No newline at end of file
+}
diff --git a/force-app/main/default/classes/AdyenRefundHelper.cls-meta.xml b/force-app/main/default/classes/AdyenRefundHelper.cls-meta.xml
index d75b058..f5e18fd 100644
--- a/force-app/main/default/classes/AdyenRefundHelper.cls-meta.xml
+++ b/force-app/main/default/classes/AdyenRefundHelper.cls-meta.xml
@@ -1,5 +1,5 @@
- 51.0
+ 60.0
Active
diff --git a/force-app/main/default/classes/TestDataFactory.cls b/force-app/main/default/classes/TestDataFactory.cls
index e4ade74..bf773ee 100644
--- a/force-app/main/default/classes/TestDataFactory.cls
+++ b/force-app/main/default/classes/TestDataFactory.cls
@@ -2,20 +2,16 @@
public class TestDataFactory {
// constants
public static final String TEST_PSP_REFERENCE = '853587067740652G';
- public static final String TEST_PSP_REFERENCE_FAIL = '853587067740652F';
- public static final String TEST_MERCHANT_ACCOUNT = 'TEST_MERCHANT_ACCOUNT';
- public static final String TEST_MERCHANT_REFERENCE = 'TEST_MERCHANT_REFERENCE';
- public static final String TEST_SHOPPER_REFERENCE = 'TEST_SHOPPER_REFERENCE';
- public static final String TEST_CARD_SUCCESS = '4242424242424242';
public static final String TEST_PAYMENT_TOKEN = 'TEST_PAYMENT_TOKEN';
public static final String TEST_AUTH_CODE = 'TEST_AUTH_CODE';
public static final String RESULT_CODE_SUCCESS = 'Authorised';
public static final String RESULT_CODE_FAIL = 'Failure';
- public static final String GATEWAY_REF = 'gatewayReference';
+ public static final String GATEWAY_REF = '0000000000000000';
public static final Double TEST_AMOUNT = 19.99;
public static final String ACTIVE_CURRENCY = [SELECT IsoCode FROM CurrencyType WHERE IsActive = TRUE LIMIT 1].IsoCode;
+ public static final String ASSERT_PRICE_MESSAGE = 'For input price of ';
- public static AdyenAsyncAdapter adyenAdapter = new AdyenAsyncAdapter();
+ public static AdyenGatewayAdapter adyenAdapter = new AdyenGatewayAdapter();
public static Account createAccount() {
return new Account(Name = 'Test Account');
@@ -99,23 +95,26 @@ public class TestDataFactory {
nri.eventCode = eventCode;
nri.pspReference = GATEWAY_REF;
nri.eventDate = String.valueOf(System.today());
- nri.paymentpspReference = 'paymentpspref';
nri.success = 'true';
- nri.merchantAccountCode = 'merchant_account_code';
+ nri.merchantAccountCode = [SELECT Merchant_Account__c FROM Adyen_Adapter__mdt LIMIT 1].Merchant_Account__c;
nri.originalReference = originalRef;
return nri;
}
public static CommercePayments.AuthorizationRequest createAuthorisationRequest(Id payMethodId) {
- CommercePayments.AuthorizationRequest authRequest = new CommercePayments.AuthorizationRequest(TEST_AMOUNT);
+ return createAuthorisationRequest(payMethodId, TEST_AMOUNT);
+ }
+
+ public static CommercePayments.AuthorizationRequest createAuthorisationRequest(Id payMethodId, Double price) {
+ CommercePayments.AuthorizationRequest authRequest = new CommercePayments.AuthorizationRequest(price);
authRequest.currencyIsoCode = ACTIVE_CURRENCY;
-
+
CommercePayments.CardPaymentMethodRequest cardPayMeth = new CommercePayments.CardPaymentMethodRequest(CommercePayments.CardCategory.CreditCard);
authRequest.paymentMethod = new CommercePayments.AuthApiPaymentMethodRequest(cardPayMeth);
authRequest.paymentMethod.id = payMethodId;
- CommercePayments.PaymentGatewayContext payGatecontext = new CommercePayments.PaymentGatewayContext(authRequest, CommercePayments.RequestType.Authorize);
- CommercePayments.PaymentGatewayRequest paymentRequest = payGatecontext.getPaymentRequest();
+ CommercePayments.PaymentGatewayContext payGateContext = new CommercePayments.PaymentGatewayContext(authRequest, CommercePayments.RequestType.Authorize);
+ CommercePayments.PaymentGatewayRequest paymentRequest = payGateContext.getPaymentRequest();
return (CommercePayments.AuthorizationRequest)paymentRequest;
}
@@ -284,39 +283,41 @@ public class TestDataFactory {
String endpoint = req.getEndpoint();
Map requestBody = (Map)JSON.deserializeUntyped(req.getBody());
-
- if(endpoint.containsIgnoreCase('payment')) {
- Map paymentMethod = (Map)JSON.deserializeUntyped(JSON.serialize((requestBody.get('paymentMethod'))));
- Object spm = requestBody.get('storePaymentMethod');
+ if (endpoint.containsIgnoreCase('capture') || endpoint.containsIgnoreCase('refund')) {
+ amount.put('value', Integer.valueOf(TEST_AMOUNT*10));
+ } else {
+ Map paymentMethod = (Map) JSON.deserializeUntyped(JSON.serialize((requestBody.get('paymentMethod'))));
responseBody.put('resultCode', RESULT_CODE_SUCCESS);
// Authorize
- if(TEST_PAYMENT_TOKEN.equals(paymentMethod.get('storedPaymentMethodId'))) { // Successful Auth
+ if (TEST_PAYMENT_TOKEN.equals(paymentMethod.get('storedPaymentMethodId'))) { // Successful Auth
additionalData.put('authCode', TEST_AUTH_CODE);
- amount.put('value', Integer.valueOf(TEST_AMOUNT*10));
+ amount.put('value', Integer.valueOf(TEST_AMOUNT * 10));
} else { // Failed Auth
responseBody.put('resultCode', RESULT_CODE_FAIL);
res.setStatusCode(AdyenConstants.HTTP_ERROR_CODE);
}
responseBody.put('additionalData', additionalData);
- } else if(endpoint.containsIgnoreCase('capture')) {
- // Capture
- responseBody.put('reference', TEST_SHOPPER_REFERENCE);
- if(requestBody.get('merchantAccount') != '' && requestBody.get('amount') != null) { // Successful Capture
- amount.put('value', Integer.valueOf(TEST_AMOUNT*10));
- } else { // Failed Capture
- res.setStatusCode(AdyenConstants.HTTP_ERROR_CODE);
- }
- } else if(endpoint.containsIgnoreCase('refund')) {
- // Refund
- responseBody.put('reference', TEST_SHOPPER_REFERENCE);
- if(requestBody.get('merchantAccount') != '' && requestBody.get('amount') != null) { // Successful Refund
- amount.put('value', Integer.valueOf(TEST_AMOUNT*10));
- } else { // Failed Refund
- res.setStatusCode(AdyenConstants.HTTP_ERROR_CODE);
- }
}
res.setBody(JSON.serialize(responseBody));
return res;
}
}
+
+ public class FailureResponse implements HttpCalloutMock {
+ public HttpResponse respond(HttpRequest req) {
+ return mockHttpResponse(genericErrorResponse(), 400);
+ }
+ }
+
+ public static HttpResponse mockHttpResponse(String body, Integer code) {
+ HttpResponse res = new HttpResponse();
+ res.setHeader('Content-Type', 'text/json');
+ res.setBody(body);
+ res.setStatusCode(code);
+ return res;
+ }
+
+ private static String genericErrorResponse() {
+ return '{"status": 400, "errorCode": "702", "message": "Empty input which would have resulted in a null result.", "errorType": "validation"}';
+ }
}
\ No newline at end of file
diff --git a/force-app/main/default/classes/TestDataFactory.cls-meta.xml b/force-app/main/default/classes/TestDataFactory.cls-meta.xml
index 9bbf7b4..f5e18fd 100644
--- a/force-app/main/default/classes/TestDataFactory.cls-meta.xml
+++ b/force-app/main/default/classes/TestDataFactory.cls-meta.xml
@@ -1,5 +1,5 @@
- 56.0
+ 60.0
Active
-
\ No newline at end of file
+
diff --git a/force-app/main/default/customMetadata/Adyen_Adapter.AdyenDefault.md-meta.xml b/force-app/main/default/customMetadata/Adyen_Adapter.AdyenDefault.md-meta.xml
index 3478fed..4c2deef 100644
--- a/force-app/main/default/customMetadata/Adyen_Adapter.AdyenDefault.md-meta.xml
+++ b/force-app/main/default/customMetadata/Adyen_Adapter.AdyenDefault.md-meta.xml
@@ -12,11 +12,7 @@
Endpoint_Api_Version__c
- /v70
-
-
- Endpoint_Method__c
- POST
+ /v71
Endpoint_Path__c
@@ -24,18 +20,18 @@
Merchant_Account__c
- MerchantAccountName
+ Merchant_Account_Name
Refund_Endpoint__c
/payments/{paymentPspReference}/refunds
- Single_Currency_Code__c
+ System_Integrator_Name__c
- System_Integrator_Name__c
-
+ HMAC_Key__c
+ Generated_HMAC_Key
diff --git a/force-app/main/default/externalCredentials/Adyen_API.externalCredential-meta.xml b/force-app/main/default/externalCredentials/Adyen_API.externalCredential-meta.xml
new file mode 100644
index 0000000..592589a
--- /dev/null
+++ b/force-app/main/default/externalCredentials/Adyen_API.externalCredential-meta.xml
@@ -0,0 +1,16 @@
+
+
+ Custom
+
+ AdyenParameterKey
+ NamedPrincipal
+ 1
+
+
+ x-API-key
+ AuthHeader
+ {!$Credential.Adyen_API.ApiKey}
+ 1
+
+
+
diff --git a/force-app/main/default/gatewayProviderPaymentMethodTypes/AlternativePaymentMethod.gatewayProviderPaymentMethodType-meta.xml b/force-app/main/default/gatewayProviderPaymentMethodTypes/AlternativePaymentMethod.gatewayProviderPaymentMethodType-meta.xml
new file mode 100644
index 0000000..ac31588
--- /dev/null
+++ b/force-app/main/default/gatewayProviderPaymentMethodTypes/AlternativePaymentMethod.gatewayProviderPaymentMethodType-meta.xml
@@ -0,0 +1,8 @@
+
+
+ AdyenComponent
+ Alternative Payment Method
+ Adyen_OMS_Provider
+ AlternativePaymentMethod
+ AlternativePaymentMethod.Alternative_Payment_Method
+
diff --git a/force-app/main/default/layouts/Adyen_Adapter__mdt-Adyen Adapter Layout.layout-meta.xml b/force-app/main/default/layouts/Adyen_Adapter__mdt-Adyen Adapter Layout.layout-meta.xml
index 24cb1e0..bab4ba4 100644
--- a/force-app/main/default/layouts/Adyen_Adapter__mdt-Adyen Adapter Layout.layout-meta.xml
+++ b/force-app/main/default/layouts/Adyen_Adapter__mdt-Adyen Adapter Layout.layout-meta.xml
@@ -1,10 +1,10 @@
- false
- false
+ true
+ true
true
-
+
Required
@@ -12,72 +12,79 @@
Required
- DeveloperName
+ Merchant_Account__c
+
+
+ Required
+ HMAC_Key__c
- Edit
- IsProtected
+ Required
+ DeveloperName
- Required
- NamespacePrefix
+ Edit
+ System_Integrator_Name__c
-
+
false
- false
+ true
true
-
+
- Edit
- Capture_Endpoint__c
+ Required
+ NamespacePrefix
Edit
- Endpoint_Api_Version__c
+ IsProtected
Edit
- Endpoint_Path__c
+ Endpoint_Api_Version__c
+
+
Edit
- Merchant_Account__c
+ Endpoint_Path__c
Edit
- Endpoint_Method__c
+ Authorize_Endpoint__c
Edit
- Authorize_Endpoint__c
+ Capture_Endpoint__c
Edit
Refund_Endpoint__c
+
+
+
+
+ false
+ true
+ true
+
+
Readonly
- CreatedById
+ LastModifiedById
-
- Edit
- Single_Currency_Code__c
-
-
- Edit
- System_Integrator_Name__c
-
Readonly
- LastModifiedById
+ CreatedById
@@ -98,7 +105,7 @@
false
false
- 00h8c00000hNsuK
+ 00h7Q00000G96rR
4
0
Default
diff --git a/force-app/main/default/layouts/Payment-Payment Layout.layout-meta.xml b/force-app/main/default/layouts/Payment-Payment Layout.layout-meta.xml
deleted file mode 100644
index b97a761..0000000
--- a/force-app/main/default/layouts/Payment-Payment Layout.layout-meta.xml
+++ /dev/null
@@ -1,254 +0,0 @@
-
-
-
- false
- false
- true
-
-
-
- Readonly
- PaymentNumber
-
-
- Edit
- AccountId
-
-
- Required
- Amount
-
-
- Edit
- PaymentAuthorizationId
-
-
- Edit
- PaymentMethodId
-
-
- Required
- ProcessingMode
-
-
- Readonly
- Balance
-
-
- Readonly
- TotalRefundApplied
-
-
- Edit
- Date
-
-
- Edit
- Adyen_Payment_Method__c
-
-
- Edit
- Adyen_Payment_Method_Variant__c
-
-
- Edit
- adyenOverrideMerchantConfig__c
-
-
-
-
- Required
- Status
-
-
- Required
- Type
-
-
- Required
- CurrencyIsoCode
-
-
- Edit
- PaymentGroupId
-
-
- Edit
- EffectiveDate
-
-
- Edit
- Comments
-
-
- Readonly
- TotalRefundUnapplied
-
-
- Readonly
- NetRefundApplied
-
-
-
-
-
- false
- true
- true
-
-
-
- Readonly
- ImpactAmount
-
-
- Edit
- CancellationDate
-
-
- Edit
- SfResultCode
-
-
- Edit
- CancellationSfResultCode
-
-
-
-
- Edit
- OrderPaymentSummaryId
-
-
- Edit
- CancellationEffectiveDate
-
-
-
-
-
- false
- true
- true
-
-
-
- Edit
- PaymentGatewayId
-
-
- Edit
- GatewayResultCode
-
-
- Edit
- GatewayRefNumber
-
-
- Edit
- CancellationGatewayDate
-
-
- Edit
- CancellationGatewayRefNumber
-
-
-
-
- Edit
- GatewayDate
-
-
- Edit
- GatewayResultCodeDescription
-
-
- Edit
- GatewayRefDetails
-
-
- Edit
- CancellationGatewayResultCode
-
-
-
-
-
- false
- true
- true
-
-
-
- Readonly
- CreatedById
-
-
- Readonly
- CreatedDate
-
-
- Edit
- MacAddress
-
-
- Edit
- Phone
-
-
-
-
- Readonly
- LastModifiedById
-
-
- Readonly
- LastModifiedDate
-
-
- Edit
- IpAddress
-
-
- Edit
- Email
-
-
-
-
-
- true
- true
- false
-
-
-
-
-
-
-
- Name
- Refund
- Amount
- Type
- Date
- RefundLinesPayment
-
-
- Name
- Invoice
- Amount
- Type
- Date
- PaymentLinesInvoice
-
- false
- false
- false
-
- 00h8c00000hPHQ1
- 4
- 0
- Default
-
-
diff --git a/force-app/main/default/layouts/PaymentAuthorization-Payment Authorization Layout.layout-meta.xml b/force-app/main/default/layouts/PaymentAuthorization-Payment Authorization Layout.layout-meta.xml
deleted file mode 100644
index 9e8dd51..0000000
--- a/force-app/main/default/layouts/PaymentAuthorization-Payment Authorization Layout.layout-meta.xml
+++ /dev/null
@@ -1,193 +0,0 @@
-
-
-
- false
- false
- true
-
-
-
- Readonly
- PaymentAuthorizationNumber
-
-
- Edit
- AccountId
-
-
- Required
- Amount
-
-
- Edit
- PaymentMethodId
-
-
- Edit
- ExpirationDate
-
-
- Required
- ProcessingMode
-
-
- Edit
- Adyen_Payment_Method__c
-
-
- Edit
- Adyen_Payment_Method_Variant__c
-
-
- Edit
- adyenOverrideMerchantConfig__c
-
-
-
-
- Required
- Status
-
-
- Edit
- Date
-
-
- Required
- CurrencyIsoCode
-
-
- Edit
- PaymentGroupId
-
-
- Edit
- EffectiveDate
-
-
- Edit
- Comments
-
-
-
-
-
- false
- true
- true
-
-
-
- Edit
- OrderPaymentSummaryId
-
-
- Readonly
- SfResultCode
-
-
-
-
-
-
- false
- true
- true
-
-
-
- Edit
- PaymentGatewayId
-
-
- Edit
- GatewayAuthCode
-
-
- Edit
- GatewayResultCode
-
-
- Edit
- GatewayRefNumber
-
-
-
-
- Edit
- GatewayDate
-
-
- Edit
- GatewayResultCodeDescription
-
-
- Edit
- GatewayRefDetails
-
-
-
-
-
- false
- true
- true
-
-
-
- Readonly
- CreatedById
-
-
- Readonly
- CreatedDate
-
-
- Edit
- MacAddress
-
-
- Edit
- Phone
-
-
-
-
- Readonly
- LastModifiedById
-
-
- Readonly
- LastModifiedDate
-
-
- Edit
- IpAddress
-
-
- Edit
- Email
-
-
-
-
-
- true
- true
- false
-
-
-
-
-
-
- false
- false
- false
-
- 00h8c00000hPHQ3
- 4
- 0
- Default
-
-
diff --git a/force-app/main/default/layouts/SalesChannel-Sales Channel Layout.layout-meta.xml b/force-app/main/default/layouts/SalesChannel-Sales Channel Layout.layout-meta.xml
deleted file mode 100644
index 7899b5d..0000000
--- a/force-app/main/default/layouts/SalesChannel-Sales Channel Layout.layout-meta.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
- true
- true
- true
-
-
-
- Required
- SalesChannelName
-
-
- Edit
- AdyenMerchantID__c
-
-
- Edit
- Description
-
-
- Edit
- ExternalChannelNumber
-
-
-
-
-
- true
- true
- false
-
-
-
-
-
-
-
- ORDERS.ORDER_NUMBER
- Orders
-
- false
- false
- false
-
- 00h8c00000hPHPt
- 4
- 0
- Default
-
-
diff --git a/force-app/main/default/namedCredentials/Adyen.namedCredential-meta.xml b/force-app/main/default/namedCredentials/Adyen.namedCredential-meta.xml
deleted file mode 100644
index c52fef0..0000000
--- a/force-app/main/default/namedCredentials/Adyen.namedCredential-meta.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- true
- false
- https://checkout-test.adyen.com
- true
-
- NamedUser
- Password
- [YOUR-WS-USER]
- [YOUR-API-KEY]
-
\ No newline at end of file
diff --git a/force-app/main/default/namedCredentials/AdyenCheckout.namedCredential-meta.xml b/force-app/main/default/namedCredentials/AdyenCheckout.namedCredential-meta.xml
new file mode 100644
index 0000000..c5c5c87
--- /dev/null
+++ b/force-app/main/default/namedCredentials/AdyenCheckout.namedCredential-meta.xml
@@ -0,0 +1,18 @@
+
+
+ false
+ true
+ false
+
+
+ Url
+ Url
+ https://checkout-test.adyen.com
+
+
+ Adyen_API
+ ExternalCredential
+ Authentication
+
+ SecuredEndpoint
+
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Authorize_Endpoint__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Authorize_Endpoint__c.field-meta.xml
index 959103d..a999874 100644
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Authorize_Endpoint__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Authorize_Endpoint__c.field-meta.xml
@@ -2,12 +2,11 @@
Authorize_Endpoint__c
"/authorize"
- false
false
- SubscriberControlled
+ DeveloperControlled
64
false
Text
false
-
\ No newline at end of file
+
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Capture_Endpoint__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Capture_Endpoint__c.field-meta.xml
index d2a0ba2..0808ddf 100644
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Capture_Endpoint__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Capture_Endpoint__c.field-meta.xml
@@ -2,9 +2,8 @@
Capture_Endpoint__c
"/capture"
- false
false
- SubscriberControlled
+ DeveloperControlled
64
false
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Api_Version__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Api_Version__c.field-meta.xml
index 176573a..1cc6afe 100644
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Api_Version__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Api_Version__c.field-meta.xml
@@ -1,11 +1,10 @@
Endpoint_Api_Version__c
- false
- The Api Version of the Endpoint.. ie. v52
+ The Api Version of the Endpoint, e.g. v71
false
- SubscriberControlled
- The Api Version of the Endpoint ie. v52
+ DeveloperControlled
+ The Api Version of the Endpoint, e.g. v71
16
false
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Method__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Method__c.field-meta.xml
deleted file mode 100644
index 2562986..0000000
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Method__c.field-meta.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- Endpoint_Method__c
- "POST"
- false
- The HTTP Method for the endpoint. ie. POST
- false
- SubscriberControlled
- The HTTP Method for the endpoint. ie. POST
-
- 16
- false
- Text
- false
-
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Path__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Path__c.field-meta.xml
index 3657a62..1109233 100644
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Path__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Endpoint_Path__c.field-meta.xml
@@ -1,10 +1,9 @@
Endpoint_Path__c
- false
The path to be appended to the Endpoint URI Listed in the Named Credential.
false
- SubscriberControlled
+ DeveloperControlled
The path to be appended to the Endpoint URI Listed in the Named Credential.
255
diff --git a/force-app/main/default/objects/Payment/fields/adyenOverrideMerchantConfig__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/HMAC_Key__c.field-meta.xml
similarity index 59%
rename from force-app/main/default/objects/Payment/fields/adyenOverrideMerchantConfig__c.field-meta.xml
rename to force-app/main/default/objects/Adyen_Adapter__mdt/fields/HMAC_Key__c.field-meta.xml
index afbd6cc..38680e6 100644
--- a/force-app/main/default/objects/Payment/fields/adyenOverrideMerchantConfig__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/HMAC_Key__c.field-meta.xml
@@ -1,9 +1,10 @@
- adyenOverrideMerchantConfig__c
+ HMAC_Key__c
false
-
- 40
+ SubscriberControlled
+
+ 255
false
Text
false
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Merchant_Account__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Merchant_Account__c.field-meta.xml
index b057651..a970d12 100644
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Merchant_Account__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Merchant_Account__c.field-meta.xml
@@ -1,7 +1,6 @@
Merchant_Account__c
- false
false
SubscriberControlled
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Refund_Endpoint__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Refund_Endpoint__c.field-meta.xml
index 421cfc9..022bbb4 100644
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Refund_Endpoint__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Refund_Endpoint__c.field-meta.xml
@@ -2,9 +2,8 @@
Refund_Endpoint__c
"/refund"
- false
false
- SubscriberControlled
+ DeveloperControlled
64
false
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Single_Currency_Code__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Single_Currency_Code__c.field-meta.xml
deleted file mode 100644
index afeb99a..0000000
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/Single_Currency_Code__c.field-meta.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- Single_Currency_Code__c
- false
- Leave blank if org is using mult-currency otherwise enter the currency ISO Code the org is using ie. USD . If null the adapter will look for xxxIsoCode fields in the org. These are placed there when enabling multi-currency
- false
- SubscriberControlled
- Leave blank if org is using mult-currency otherwise enter the currency ISO Code the org is using ie. USD
-
- 4
- false
- Text
- false
-
diff --git a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/System_Integrator_Name__c.field-meta.xml b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/System_Integrator_Name__c.field-meta.xml
index 999accf..6cb2ab7 100644
--- a/force-app/main/default/objects/Adyen_Adapter__mdt/fields/System_Integrator_Name__c.field-meta.xml
+++ b/force-app/main/default/objects/Adyen_Adapter__mdt/fields/System_Integrator_Name__c.field-meta.xml
@@ -1,7 +1,6 @@
System_Integrator_Name__c
- false
Name of the System Integrator, working with the Merchant on installing and enabling the package
false
SubscriberControlled
diff --git a/force-app/main/default/objects/AlternativePaymentMethod/recordTypes/Alternative_Payment_Method.recordType-meta.xml b/force-app/main/default/objects/AlternativePaymentMethod/recordTypes/Alternative_Payment_Method.recordType-meta.xml
new file mode 100644
index 0000000..362b747
--- /dev/null
+++ b/force-app/main/default/objects/AlternativePaymentMethod/recordTypes/Alternative_Payment_Method.recordType-meta.xml
@@ -0,0 +1,6 @@
+
+
+ Alternative_Payment_Method
+ true
+
+
diff --git a/force-app/main/default/objects/PaymentAuthorization/fields/adyenOverrideMerchantConfig__c.field-meta.xml b/force-app/main/default/objects/PaymentAuthorization/fields/adyenOverrideMerchantConfig__c.field-meta.xml
deleted file mode 100644
index afbd6cc..0000000
--- a/force-app/main/default/objects/PaymentAuthorization/fields/adyenOverrideMerchantConfig__c.field-meta.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- adyenOverrideMerchantConfig__c
- false
-
- 40
- false
- Text
- false
-
diff --git a/force-app/main/default/objects/SalesChannel/fields/AdyenMerchantID__c.field-meta.xml b/force-app/main/default/objects/SalesChannel/fields/AdyenMerchantID__c.field-meta.xml
index 5b11ec0..c35b047 100644
--- a/force-app/main/default/objects/SalesChannel/fields/AdyenMerchantID__c.field-meta.xml
+++ b/force-app/main/default/objects/SalesChannel/fields/AdyenMerchantID__c.field-meta.xml
@@ -3,7 +3,7 @@
AdyenMerchantID__c
false
false
- Must match the custom metadata record name. The actual Adyen merchant ID is pulled from metadata config
+ The merchant account name for this Sales Channel. If left blank, the Adyen Default custom metadata record is used.
50
false
diff --git a/force-app/main/default/paymentGatewayProviders/Adyen_OMS_Provider.paymentGatewayProvider-meta.xml b/force-app/main/default/paymentGatewayProviders/Adyen_OMS_Provider.paymentGatewayProvider-meta.xml
new file mode 100644
index 0000000..a1a215b
--- /dev/null
+++ b/force-app/main/default/paymentGatewayProviders/Adyen_OMS_Provider.paymentGatewayProvider-meta.xml
@@ -0,0 +1,6 @@
+
+
+ AdyenGatewayAdapter
+ Yes
+ Adyen OMS Provider
+
diff --git a/force-app/main/default/profiles/Admin.profile-meta.xml b/force-app/main/default/profiles/Admin.profile-meta.xml
index 7e9edf3..254386f 100644
--- a/force-app/main/default/profiles/Admin.profile-meta.xml
+++ b/force-app/main/default/profiles/Admin.profile-meta.xml
@@ -1,5 +1,57 @@
+
+ AdyenAsyncAdapter
+ true
+
+
+ AdyenGatewayAdapter
+ true
+
+
+ AdyenGatewayAdapterTest
+ true
+
+
+ AdyenAuthorisationHelper
+ true
+
+
+ AdyenAuthorisationHelperTest
+ true
+
+
+ AdyenCaptureHelper
+ true
+
+
+ AdyenOMSConstants
+ true
+
+
+ AdyenPaymentHelper
+ true
+
+
+ AdyenPaymentHelperTest
+ true
+
+
+ AdyenPaymentUtility
+ true
+
+
+ AdyenPaymentUtilityTest
+ true
+
+
+ AdyenRefundHelper
+ true
+
+
+ TestDataFactory
+ true
+
false
true
@@ -11,11 +63,6 @@
Payment.Adyen_Payment_Method__c
true
-
- true
- Payment.adyenOverrideMerchantConfig__c
- true
-
true
PaymentAuthorization.Adyen_Payment_Method_Variant__c
@@ -28,762 +75,42 @@
true
- PaymentAuthorization.adyenOverrideMerchantConfig__c
+ SalesChannel.AdyenMerchantID__c
true
- Payment-Payment Layout
+ Adyen_Adapter__mdt-Adyen Adapter Layout
-
- PaymentAuthorization-Payment Authorization Layout
-
- Salesforce
-
- true
- AIViewInsightObjects
-
-
- true
- ActivateContract
-
-
- true
- ActivateOrder
-
-
- true
- ActivitiesAccess
-
-
- true
- AddDirectMessageMembers
-
-
- true
- AllowUniversalSearch
-
-
- true
- AllowViewKnowledge
-
-
- true
- ApexRestServices
-
-
- true
- ApiEnabled
-
-
- true
- AssignPermissionSets
-
-
- true
- AssignTopics
-
-
- true
- AuthorApex
-
-
- true
- BulkMacrosAllowed
-
-
- true
- CanAccessCE
-
-
- true
- CanInsertFeedSystemFields
-
-
- true
- CanUseNewDashboardBuilder
-
-
- true
- CanVerifyComment
-
-
- true
- ChangeDashboardColors
-
-
- true
- ChatterEditOwnPost
-
-
- true
- ChatterEditOwnRecordPost
-
-
- true
- ChatterFileLink
-
-
- true
- ChatterInternalUser
-
-
- true
- ChatterInviteExternalUsers
-
-
- true
- ChatterOwnGroups
-
-
- true
- ClientSecretRotation
-
-
- true
- ConnectOrgToEnvironmentHub
-
-
- true
- ConsentApiUpdate
-
-
- true
- ContentAdministrator
-
-
- true
- ContentWorkspaces
-
-
- true
- ConvertLeads
-
-
- true
- CreateCustomizeDashboards
-
-
- true
- CreateCustomizeFilters
-
-
- true
- CreateCustomizeReports
-
-
- true
- CreateDashboardFolders
-
-
- true
- CreateLtngTempFolder
-
-
- true
- CreatePackaging
-
-
- true
- CreateReportFolders
-
-
- true
- CreateTopics
-
-
- true
- CreateWorkspaces
-
-
- true
- CustomizeApplication
-
-
- true
- DataExport
-
-
- true
- DelegatedTwoFactor
-
-
- true
- DeleteActivatedContract
-
-
- true
- DeleteTopics
-
-
- true
- DistributeFromPersWksp
-
-
- true
- EditActivatedOrders
-
-
- true
- EditBillingInfo
-
-
- true
- EditBrandTemplates
-
-
- true
- EditCaseComments
-
-
- true
- EditDeliveryInformation
-
-
- true
- EditEvent
-
-
- true
- EditHtmlTemplates
-
-
- true
- EditKnowledge
-
-
- true
- EditMyDashboards
-
-
- true
- EditMyReports
-
-
- true
- EditOppLineItemUnitPrice
-
-
- true
- EditPublicDocuments
-
-
- true
- EditPublicFilters
-
-
- true
- EditPublicTemplates
-
-
- true
- EditReadonlyFields
-
-
- true
- EditTask
-
-
- true
- EditTopics
-
-
- true
- EmailMass
-
-
- true
- EmailSingle
-
-
- true
- EnableCommunityAppLauncher
-
-
- true
- EnableNotifications
-
-
- true
- ExportReport
-
-
- true
- GiveRecognitionBadge
-
-
- true
- ImportCustomObjects
-
-
- true
- ImportLeads
-
-
- true
- ImportPersonal
-
-
- true
- InstallPackaging
-
-
- true
- LightningConsoleAllowedForUser
-
-
- true
- LightningExperienceUser
-
-
- true
- ListEmailSend
-
-
- true
- ManageAnalyticSnapshots
-
-
- true
- ManageAuthProviders
-
-
- true
- ManageBusinessHourHolidays
-
-
- true
- ManageC360AConnections
-
-
- true
- ManageCMS
-
-
- true
- ManageCallCenters
-
-
- true
- ManageCases
-
-
- true
- ManageCategories
-
-
- true
- ManageCertificates
-
-
- true
- ManageContentPermissions
-
-
- true
- ManageContentProperties
-
-
- true
- ManageContentTypes
-
-
- true
- ManageCustomPermissions
-
-
- true
- ManageCustomReportTypes
-
-
- true
- ManageDashbdsInPubFolders
-
-
- true
- ManageDataCategories
-
-
- true
- ManageDataIntegrations
-
-
- true
- ManageDynamicDashboards
-
-
- true
- ManageEmailClientConfig
-
-
- true
- ManageEntitlements
-
-
- true
- ManageExchangeConfig
-
-
- true
- ManageHealthCheck
-
-
- true
- ManageHubConnections
-
-
- true
- ManageInteraction
-
-
- true
- ManageInternalUsers
-
-
- true
- ManageIpAddresses
-
-
- true
- ManageKnowledge
-
-
- true
- ManageKnowledgeImportExport
-
-
- true
- ManageLeads
-
-
- true
- ManageLoginAccessPolicies
-
-
- true
- ManageMobile
-
-
- true
- ManageNetworks
-
-
- true
- ManageOrchInstsAndWorkItems
-
-
- true
- ManagePackageLicenses
-
-
- true
- ManagePasswordPolicies
-
-
- true
- ManageProfilesPermissionsets
-
-
- true
- ManagePropositions
-
-
- true
- ManagePvtRptsAndDashbds
-
-
- true
- ManageRecommendationStrategies
-
-
- true
- ManageReleaseUpdates
-
-
- true
- ManageRemoteAccess
-
-
- true
- ManageReportsInPubFolders
-
-
- true
- ManageRoles
-
-
- true
- ManageSearchPromotionRules
-
-
- true
- ManageSharing
-
-
- true
- ManageSolutions
-
-
- true
- ManageSubscriptions
-
-
- true
- ManageSynonyms
-
-
- true
- ManageUnlistedGroups
-
-
- true
- ManageUsers
-
-
- true
- MassInlineEdit
-
-
- true
- MergeTopics
-
-
- true
- ModerateChatter
-
-
- true
- ModifyAllData
-
-
- true
- ModifyDataClassification
-
-
- true
- ModifyMetadata
-
-
- true
- NewReportBuilder
-
-
- true
- OmnichannelInventorySync
-
-
- true
- Packaging2
-
-
- true
- Packaging2Delete
-
-
- true
- PrivacyDataAccess
-
-
- true
- PublishPackaging
-
-
- true
- RemoveDirectMessageMembers
-
-
- true
- ResetPasswords
-
-
- true
- RunReports
-
-
- true
- ScheduleReports
-
-
- true
- SelectFilesFromSalesforce
-
-
- true
- SendCustomNotifications
-
-
- true
- SendSitRequests
-
-
- true
- ShareInternalArticles
-
-
- true
- ShowCompanyNameAsUserBadge
-
-
- true
- SolutionImport
-
-
- true
- SubmitMacrosAllowed
-
-
- true
- SubscribeDashboardRolesGrps
-
-
- true
- SubscribeDashboardToOtherUsers
-
-
- true
- SubscribeReportRolesGrps
-
-
- true
- SubscribeReportToOtherUsers
-
-
- true
- SubscribeReportsRunAsUser
-
-
- true
- SubscribeToLightningDashboards
-
-
- true
- SubscribeToLightningReports
-
-
- true
- TransactionalEmailSend
-
-
- true
- TransferAnyCase
-
-
- true
- TransferAnyEntity
-
-
- true
- TransferAnyLead
-
-
- true
- UseFulfillmentAPIs
-
-
- true
- UseOmnichannelInventoryAPIs
-
-
- true
- UseOrderManagementAPIs
-
-
- true
- UseReturnOrder
-
-
- true
- UseReturnOrderAPIs
-
-
- true
- UseShipment
-
-
- true
- UseTeamReassignWizards
-
-
- true
- UseWebLink
-
-
- true
- ViewAllData
-
-
- true
- ViewAllProfiles
-
-
- true
- ViewAllUsers
-
-
- true
- ViewDataAssessment
-
-
- true
- ViewDataCategories
-
-
- true
- ViewDataLeakageEvents
-
-
- true
- ViewDeveloperName
-
-
- true
- ViewEventLogFiles
-
-
- true
- ViewFlowUsageAndFlowEventData
-
-
- true
- ViewHealthCheck
-
-
- true
- ViewHelpLink
-
-
- true
- ViewMLModels
-
-
- true
- ViewMyTeamsDashboards
-
-
- true
- ViewPlatformEvents
-
-
- true
- ViewPublicDashboards
-
-
- true
- ViewPublicReports
-
-
- true
- ViewRoles
-
-
- true
- ViewSetup
-
-
- true
- ViewUserPII
-
-
- true
- WorkCalibrationUser
-
+
+ true
+ true
+ true
+ true
+ true
+
+ true
+
+
+ true
+ true
+ true
+ true
+ true
+
+ true
+
+
+ true
+ true
+ true
+ true
+ true
+
+ true
+
+
+ true
+ AlternativePaymentMethod.Alternative_Payment_Method
+ true
+
diff --git a/manifest/package.xml b/manifest/package.xml
new file mode 100644
index 0000000..c48061a
--- /dev/null
+++ b/manifest/package.xml
@@ -0,0 +1,75 @@
+
+
+
+ AdyenAsyncAdapter
+ AdyenAuthorisationHelper
+ AdyenAuthorisationHelperTest
+ AdyenCaptureHelper
+ AdyenOMSConstants
+ AdyenPaymentHelper
+ AdyenPaymentHelperTest
+ AdyenPaymentUtility
+ AdyenPaymentUtilityTest
+ AdyenRefundHelper
+ TestDataFactory
+ AdyenGatewayAdapter
+ AdyenGatewayAdapterTest
+ ApexClass
+
+
+ Adyen_Adapter.AdyenDefault
+ CustomMetadata
+
+
+ Adyen_Adapter__mdt
+ AlternativePaymentMethod
+ Payment
+ PaymentAuthorization
+ CustomObject
+
+
+ SalesChannel.AdyenMerchantID__c
+ Adyen_Adapter__mdt.Authorize_Endpoint__c
+ Adyen_Adapter__mdt.Capture_Endpoint__c
+ Adyen_Adapter__mdt.Endpoint_Api_Version__c
+ Adyen_Adapter__mdt.Endpoint_Path__c
+ Adyen_Adapter__mdt.HMAC_Key__c
+ Adyen_Adapter__mdt.Merchant_Account__c
+ Adyen_Adapter__mdt.Refund_Endpoint__c
+ Adyen_Adapter__mdt.System_Integrator_Name__c
+ Payment.Adyen_Payment_Method__c
+ Payment.Adyen_Payment_Method_Variant__c
+ PaymentAuthorization.Adyen_Payment_Method__c
+ PaymentAuthorization.Adyen_Payment_Method_Variant__c
+ CustomField
+
+
+ Adyen_Adapter__mdt-Adyen Adapter Layout
+ Layout
+
+
+ AdyenCheckout
+ NamedCredential
+
+
+ Admin
+ Profile
+
+
+ *
+ ExternalCredential
+
+
+ *
+ PaymentGatewayProvider
+
+
+ AlternativePaymentMethod
+ GatewayProviderPaymentMethodType
+
+
+ AlternativePaymentMethod.Alternative_Payment_Method
+ RecordType
+
+ 60.0
+
diff --git a/sfdx-project.json b/sfdx-project.json
index 4ad3835..43fdc80 100644
--- a/sfdx-project.json
+++ b/sfdx-project.json
@@ -5,25 +5,26 @@
"default": true,
"package": "Adyen Salesforce Order Management",
"definitionFile": "config/project-scratch-def.json",
- "versionName": "version 2.1",
- "versionNumber": "2.1.2.NEXT",
+ "versionName": "version 3.0",
+ "versionNumber": "3.0.0.NEXT",
"ancestorVersion": "HIGHEST",
"dependencies": [
{
- "package": "API Library Apex Adyen@3.0.1-1"
+ "package": "API Library Apex Adyen@3.2.0-5"
}
]
}
],
"namespace": "adyen_payment",
"sfdcLoginUrl": "https://login.salesforce.com",
- "sourceApiVersion": "57.0",
+ "sourceApiVersion": "60.0",
"packageAliases": {
"Adyen Salesforce Order Management": "0Ho4T000000blPMSAY",
- "API Library Apex Adyen@3.0.1-1": "04tRP0000000A5pYAE",
+ "API Library Apex Adyen@3.2.0-5": "04tRP0000000lwbYAA",
"Adyen Salesforce Order Management@2.0.0": "04t4T000001y7C0QAI",
"Adyen Salesforce Order Management@2.1.0": "04tRP00000002BVYAY",
"Adyen Salesforce Order Management@2.1.1-1": "04tRP0000000A7RYAU",
- "Adyen Salesforce Order Management@2.1.2-1": "04tRP0000000AFVYA2"
+ "Adyen Salesforce Order Management@2.1.2-1": "04tRP0000000AFVYA2",
+ "Adyen Salesforce Order Management@3.0.0-6": "04tRP0000000mW5YAI"
}
}
\ No newline at end of file