Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Choose payment screen OOBO flow for PBL #61

Merged
merged 7 commits into from
Oct 18, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: payment screen selection and error handling plus unit tests
dcardos committed Oct 9, 2024
commit 43cb1634b27d11d950f389fb4d930be5c6b1922e
48 changes: 46 additions & 2 deletions force-app/main/default/classes/AdyenOOBOController.cls
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
public with sharing class AdyenOOBOController {

@AuraEnabled(cacheable=true)
@AuraEnabled(Cacheable=true)
public static Id getOrderSummaryIdByOrderNumber(String orderNumber) {
try {
List<OrderSummary> orderSummaries = [
@@ -19,4 +19,48 @@ public with sharing class AdyenOOBOController {
throw new AuraHandledException(e.getMessage());
}
}
}

@AuraEnabled(Cacheable=true)
public static Decimal getExpiryDuration() {
try {
Decimal expiryDurationDays = [SELECT Payment_Link_Expiry_Duration__c FROM Adyen_Adapter__mdt WHERE DeveloperName = :AdyenConstants.DEFAULT_ADAPTER_NAME].Payment_Link_Expiry_Duration__c;
if (expiryDurationDays == null) {
expiryDurationDays = 1;
}
return expiryDurationDays;
} catch (Exception ex) {
throw new AuraHandledException('Unable to fetch ' + AdyenConstants.DEFAULT_ADAPTER_NAME + ' custom metadata Adyen Adapter record. Error: ' + ex.getMessage());
}
}

@AuraEnabled(Cacheable=true)
public static AccountEmailAndName getAccountEmailAndName(Id accountId) {
try {
if (AdyenPaymentUtility.personAccountsEnabled()) {
Account accountRecord = Database.query('SELECT Name, PersonEmail FROM Account WHERE Id = :accountId');
return new AccountEmailAndName(accountRecord.Name, (String)accountRecord.get('PersonEmail'));
} else {
List<Contact> contactRecords = [SELECT Name, Email FROM Contact WHERE AccountId = :accountId AND Email <> NULL ORDER BY CreatedDate DESC];
if (contactRecords.size() >= 1) {
return new AccountEmailAndName(contactRecords[0].Name, contactRecords[0].Email);
} else {
throw new AuraHandledException('No contact found for this account id ' + accountId);
}
}
} catch (Exception ex) {
throw new AuraHandledException('Unable to fetch account email. Error: ' + ex.getMessage());
}
}

public class AccountEmailAndName {
@AuraEnabled
public String name;
@AuraEnabled
public String email;

public AccountEmailAndName(String name, String email) {
this.name = name;
this.email = email;
}
}
}
36 changes: 34 additions & 2 deletions force-app/main/default/classes/AdyenOOBOControllerTest.cls
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@isTest
@IsTest
public class AdyenOOBOControllerTest {

@IsTest(SeeAllData=true)
@@ -40,4 +40,36 @@ public class AdyenOOBOControllerTest {
}
Test.stopTest();
}
}

@IsTest
static void getExpiryDurationTest() {
// given
// when
Decimal expireDays = AdyenOOBOController.getExpiryDuration();
// then
Assert.areEqual(1, expireDays);
}

@IsTest
static void getAccountEmailAndNameTest() {
// given
Contact newContact = TestDataFactory.insertAccountWithContact();
newContact = [SELECT Name, Email, AccountId FROM Contact WHERE Id = :newContact.Id];
// when
AdyenOOBOController.AccountEmailAndName acctInfo = AdyenOOBOController.getAccountEmailAndName(newContact.AccountId);
// then
Assert.areEqual(newContact.Email, acctInfo.email);
Assert.areEqual(newContact.Name, acctInfo.name);
}

@IsTest
static void getAccountEmailAndNameErrorTest() {
// given - no account
try { // when
AdyenOOBOController.getAccountEmailAndName(null);
Assert.fail();
} catch (Exception ex) { // then
Assert.isInstanceOfType(ex, AuraHandledException.class);
}
}
}
8 changes: 4 additions & 4 deletions force-app/main/default/classes/AdyenPaymentHelper.cls
Original file line number Diff line number Diff line change
@@ -180,16 +180,16 @@ public with sharing class AdyenPaymentHelper {
}

public class PBLPostAuthRequest {
@InvocableVariable(required=true)
@InvocableVariable(Required=true)
public String paymentGatewayId;

@InvocableVariable(required=true)
@InvocableVariable(Required=true)
public String accountId;

@InvocableVariable(required=true)
@InvocableVariable(Required=true)
public String currencyIsoCode;

@InvocableVariable(required=true)
@InvocableVariable(Required=true)
public Decimal amount;
}

4 changes: 4 additions & 0 deletions force-app/main/default/classes/AdyenPaymentUtility.cls
Original file line number Diff line number Diff line change
@@ -390,4 +390,8 @@ public with sharing class AdyenPaymentUtility {
return response;
}
}

public static Boolean personAccountsEnabled() {
return Schema.SObjectType.Account.fields.getMap().containsKey( 'isPersonAccount' );
}
}
8 changes: 8 additions & 0 deletions force-app/main/default/classes/TestDataFactory.cls
Original file line number Diff line number Diff line change
@@ -17,6 +17,14 @@ public class TestDataFactory {
return new Account(Name = 'Test Account');
}

public static Contact insertAccountWithContact() {
Account newAccount = createAccount();
insert newAccount;
Contact newContact = new Contact(FirstName = 'Test', LastName = 'last', AccountId = newAccount.Id, Email = '[email protected]');
insert newContact;
return newContact;
}

public static CardPaymentMethod createCardPaymentMethod() {
return new CardPaymentMethod(
GatewayToken = TEST_PAYMENT_TOKEN,
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { LightningElement, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { NavigationMixin } from 'lightning/navigation';
import { PAYMENT_MODES } from 'c/payByLinkUtils';
import getOrderSummaryIdByOrderNumber from '@salesforce/apex/AdyenOOBOController.getOrderSummaryIdByOrderNumber';
export default class AdyenPblConfirmationScreen extends NavigationMixin(LightningElement) {
PAYMENT_MODES = {
PBL: 'pbl',
CARD: 'card',
};

export default class AdyenPblConfirmationScreen extends NavigationMixin(LightningElement) {
@api shopperName;
@api paymentLink;
@api orderReference;
@@ -30,11 +27,11 @@ export default class AdyenPblConfirmationScreen extends NavigationMixin(Lightnin
}

get isPblPaymentMode() {
return this.paymentMode === this.PAYMENT_MODES.PBL;
return this.paymentMode === PAYMENT_MODES.PBL;
}

get isCardPaymentMode() {
return this.paymentMode === this.PAYMENT_MODES.CARD;
return this.paymentMode === PAYMENT_MODES.CARD;
}

async openOrderSummary() {
@@ -101,4 +98,4 @@ export default class AdyenPblConfirmationScreen extends NavigationMixin(Lightnin
formattedDate = formattedDate.replace(day, ordinalDay);
return `${formattedTime}, ${formattedDate}`;
}
}
}
Original file line number Diff line number Diff line change
@@ -8,10 +8,13 @@
</lightning-layout-item>
</lightning-layout>
<lightning-spinner lwc:if={isLoading}></lightning-spinner>
<template lwc:elseif={error}>
<c-error-panel errors={error}></c-error-panel>
</template>
<template lwc:else>
<lightning-layout multiple-rows lwc:if={pblSelected}>
<lightning-layout-item size="12" padding="horizontal-small">
<lightning-input disabled type="email" label="Sent to" value={shopperEmail}></lightning-input>
<lightning-input disabled type="email" label="Shopper email" value={shopperEmail}></lightning-input>
</lightning-layout-item>
<lightning-layout-item size="12" padding="horizontal-small">
<div class="slds-var-p-around_medium slds-text-align_center" style="background-color: #D8E6FE; color: #0B5CAB">
49 changes: 33 additions & 16 deletions force-app/main/default/lwc/choosePayment/choosePayment.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { LightningElement, wire } from 'lwc';
import getExpirationDays from '@salesforce/apex/NonPaymentWebhookHandler.getExpiryDuration';
import { LightningElement, api } from 'lwc';
import getExpirationDays from '@salesforce/apex/AdyenOOBOController.getExpiryDuration';
import getAccountNameAndEmail from '@salesforce/apex/AdyenOOBOController.getAccountEmailAndName';
import { PAYMENT_MODES } from 'c/payByLinkUtils';

export default class ChoosePayment extends LightningElement {
pblSelected = true;
expirationDays;
shopperEmail = "[email protected]";
error;
isLoading;
@api accountId;
@api shopperEmail;
@api shopperName;
@api paymentMode;

get pblButtonVariant() {
return this.pblSelected ? "brand" : "neutral";
@@ -14,28 +21,38 @@ export default class ChoosePayment extends LightningElement {
return this.pblSelected ? "neutral" : "brand";
}

get isLoading() {
if (this.pblSelected) {
return this.expirationDays == null;
} else {
return false;
async connectedCallback() {
this.isLoading = true;
this.handlePblButtonClick(); // initializing values
try {
await Promise.all([
this.fetchExpirationDays(),
this.fetchShopperInfo(),
]);
} catch (error) {
this.error = error;
} finally {
this.isLoading = false;
}
}

@wire(getExpirationDays)
wiredExpirationDays({ error, data }) {
if (data) {
this.expirationDays = data;
} else if (error) {
console.error('Error fetching expiration days', error);
}
async fetchExpirationDays() {
this.expirationDays = await getExpirationDays();
}

async fetchShopperInfo() {
const shopperInfo = await getAccountNameAndEmail({ accountId: this.accountId });
this.shopperName = shopperInfo.name;
this.shopperEmail = shopperInfo.email;
}

handlePblButtonClick () {
this.pblSelected = true;
this.paymentMode = PAYMENT_MODES.PBL;
}

handleCardButtonClick () {
this.pblSelected = false;
this.paymentMode = PAYMENT_MODES.CARD;
}
}
}
Original file line number Diff line number Diff line change
@@ -11,5 +11,13 @@
<target>lightningCommunity__Page</target>
<target>lightningCommunity__Default</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__FlowScreen">
<property name="accountId" type="String" label="Account Id" />
<property name="shopperName" type="String" label="Shopper Name" />
<property name="shopperEmail" type="String" label="Shopper Email" />
<property name="paymentMode" type="String" label="Payment Mode" />
</targetConfig>
</targetConfigs>
<masterLabel>Choose Payment</masterLabel>
</LightningComponentBundle>
28 changes: 28 additions & 0 deletions force-app/main/default/lwc/errorPanel/errorPanel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { LightningElement, api } from 'lwc';
import { reduceErrors } from 'c/ldsUtils';
import noDataIllustration from './templates/noDataIllustration.html';
import inlineMessage from './templates/inlineMessage.html';

export default class ErrorPanel extends LightningElement {
viewDetails = false;

/** Single or array of LDS errors */
@api errors;
/** Generic / user-friendly message */
@api friendlyMessage = 'An error has occurred.';
/** Type of error message **/
@api type;

get errorMessages() {
return reduceErrors(this.errors);
}

handleShowDetailsClick() {
this.viewDetails = !this.viewDetails;
}

render() {
if (this.type === 'inlineMessage') return inlineMessage;
return noDataIllustration;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>61.0</apiVersion>
<description>Error Panel</description>
<isExposed>false</isExposed>
<masterLabel>Error Panel</masterLabel>
</LightningComponentBundle>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div class="slds-var-m-vertical_small">
<span class="slds-text-color_destructive">
{friendlyMessage}.
<template if:true={errorMessages.length}>
<a onclick={handleShowDetailsClick}> Show details.</a>
</template>
</span>
<template if:true={errorMessages.length}>
<template if:true={viewDetails}>
<template for:each={errorMessages} for:item="message">
<p class="slds-text-body_regular" key={message}>
{message}
</p>
</template>
</template>
</template>
</div>
</template>
Loading