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

[Shopify] Dispute processing in Shopify (Payments module) #25753

Merged
merged 29 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9ee36ca
Add Dispute Status and "Dispute Finalized On" DateTime
daniloziva Jan 19, 2024
2a6db49
Fix object name
daniloziva Jan 19, 2024
c6d40b2
Change protection level of Shfy Payment Transaction type
daniloziva Jan 19, 2024
12c822c
Add function DisputesSync() to BackgroundSyncs.Codeunit.al
daniloziva Jan 22, 2024
5159e23
removed from Sync All Action
daniloziva Jan 22, 2024
5cdc133
Tests + CU updates to cater for test implementation
daniloziva Jan 22, 2024
6271f59
Fixes asked for by reviewer
daniloziva Jan 22, 2024
194d366
Updates
daniloziva Jan 26, 2024
d155514
remove variable
daniloziva Jan 26, 2024
106d295
Merge branch 'main' into main
daniloziva Jan 26, 2024
0564ed7
revert versions
daniloziva Jan 26, 2024
e658856
Merge branch 'main' of https://github.com/danilovetatek/ALAppExtensions
daniloziva Jan 26, 2024
17de3d9
Remove Unused var
daniloziva Jan 26, 2024
a66c64f
Add table for Disputes
daniloziva Jan 26, 2024
9717da9
Fix tests
daniloziva Jan 26, 2024
aa14d4c
CodeCopFix
daniloziva Jan 26, 2024
9e9fd0e
fix error
daniloziva Jan 26, 2024
7a2b221
Remove stray code
daniloziva Jan 28, 2024
87cc722
Merge branch 'microsoft:main' into main
daniloziva Feb 1, 2024
a5f0999
Updated enums
daniloziva Feb 5, 2024
6887705
Merge branch 'main' of https://github.com/danilovetatek/ALAppExtensions
daniloziva Feb 5, 2024
476fba8
Merge branch 'main' into main
JesperSchulz Apr 8, 2024
087928e
Apply suggestions from code review
JesperSchulz Apr 8, 2024
a32097d
Update Apps/W1/Shopify/app/src/Payments/Codeunits/ShpfyPayments.Codeu…
JesperSchulz Apr 8, 2024
d461c0c
Update Apps/W1/Shopify/app/src/Payments/Codeunits/ShpfyPayments.Codeu…
JesperSchulz Apr 9, 2024
225d716
Update Apps/W1/Shopify/test/Payments/ShpfyPaymentsTest.Codeunit.al
JesperSchulz Apr 9, 2024
c6770b1
Update test as checked in in NAV repo.
JesperSchulz Apr 10, 2024
39c5d7a
Update Apps/W1/Shopify/app/src/Payments/Codeunits/ShpfyPayments.Codeu…
JesperSchulz Apr 10, 2024
19aaf1d
Apply suggestions from internal check-in.
JesperSchulz Apr 10, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,37 @@ codeunit 30101 "Shpfy Background Syncs"
end;
end;

internal procedure DisputesSync(ShopCode: Code[20])
var
Shop: Record "Shpfy Shop";
begin
if Shop.Get(ShopCode) then begin
Shop.SetRecFilter();
DisputesSync(Shop);
end;
end;

/// <summary>
/// Payment Dispute Sync.
/// </summary>
/// <param name="Shop">Parameter of type Record "Shopify Shop".</param>
internal procedure DisputesSync(var Shop: Record "Shpfy Shop")
var
Parameters: text;
PaymentParametersTxt: Label '<?xml version="1.0" standalone="yes"?><ReportParameters name="Shpfy Sync Disputes" id="30105"><DataItems><DataItem name="Shop">%1</DataItem></DataItems></ReportParameters>', Comment = '%1 = Shop Record View', Locked = true;
begin
Shop.SetRange("Allow Background Syncs", true);
if not Shop.IsEmpty then begin
Parameters := StrSubstNo(PaymentParametersTxt, Shop.GetView());
EnqueueJobEntry(Report::"Shpfy Sync Disputes", Parameters, StrSubstNo(SyncDescriptionTxt, PayoutsSyncTypeTxt, Shop.GetFilter(Code)), true, true);
end;
Shop.SetRange("Allow Background Syncs", false);
if not Shop.IsEmpty then begin
Parameters := StrSubstNo(PaymentParametersTxt, Shop.GetView());
EnqueueJobEntry(Report::"Shpfy Sync Disputes", Parameters, StrSubstNo(SyncDescriptionTxt, PayoutsSyncTypeTxt, Shop.GetFilter(Code)), false, true);
end;
end;

/// <summary>
/// Product Images Sync.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions Apps/W1/Shopify/app/src/Base/Pages/ShpfyShopCard.Page.al
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,23 @@ page 30101 "Shpfy Shop Card"
Report.Run(Report::"Shpfy Sync Shipm. to Shopify");
end;
}
action(SyncDisputes)
{
ApplicationArea = All;
Caption = 'Sync Disputes';
Image = ErrorLog;
Promoted = true;
PromotedCategory = Category5;
PromotedOnly = true;
ToolTip = 'Synchronize dispute information with related payment transactions.';

trigger OnAction()
var
BackgroundSyncs: Codeunit "Shpfy Background Syncs";
begin
BackgroundSyncs.DisputesSync(Rec.Code);
end;
}
action(SyncAll)
{
ApplicationArea = All;
Expand Down
15 changes: 15 additions & 0 deletions Apps/W1/Shopify/app/src/Order handling/Pages/ShpfyOrder.Page.al
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,21 @@ page 30113 "Shpfy Order"
Page.Run(Page::"Shpfy Data Capture List", DataCapture);
end;
}
action(Disputes)
{
ApplicationArea = All;
Caption = 'Show Related Disputes';
Image = Entry;
ToolTip = 'View the disputes related to order of the selected transaction.';

trigger OnAction();
var
Dispute: Record "Shpfy Dispute";
begin
Dispute.SetRange("Source Order Id", Rec."Shopify Order Id");
Page.Run(Page::"Shpfy Disputes", Dispute);
end;
}
}
}

Expand Down
106 changes: 106 additions & 0 deletions Apps/W1/Shopify/app/src/Payments/Codeunits/ShpfyPayments.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,110 @@ codeunit 30169 "Shpfy Payments"
end;
end;
end;

internal procedure ImportNewDisputes()
var
Dispute: Record "Shpfy Dispute";
SinceId: BigInteger;
JDisputes: JsonArray;
JItem: JsonToken;
JResponse: JsonToken;
Url: Text;
UrlTxt: Label 'shopify_payments/disputes.json?since_id=%1', Locked = true;
begin
if Dispute.FindLast() then
SinceId := Dispute.Id;

Url := CommunicationMgt.CreateWebRequestURL(StrSubstNo(UrlTxt, SinceId));

repeat
JResponse := CommunicationMgt.ExecuteWebRequest(Url, 'GET', JResponse, Url);

if JsonHelper.GetJsonArray(JResponse, JDisputes, 'disputes') then
foreach JItem in JDisputes do
ImportDisputeData(JItem);
until Url = '';
end;

internal procedure UpdateUnfinishedDisputes()
var
Dispute: Record "Shpfy Dispute";
DisputeToken: JsonToken;
JResponse: JsonToken;
Url: Text;
UrlTxt: Label 'shopify_payments/disputes/%1.json', Locked = true;
begin
Dispute.SetFilter("Status", '<>%1&<>%2', Dispute."Status"::Won, Dispute."Status"::Lost);

if Dispute.FindSet() then
repeat
Url := CommunicationMgt.CreateWebRequestURL(StrSubstNo(UrlTxt, Dispute.Id));
JResponse := CommunicationMgt.ExecuteWebRequest(Url, 'GET', JResponse);
DisputeToken := JsonHelper.GetJsonToken(JResponse, 'dispute');
ImportDisputeData(DisputeToken);
until Dispute.Next() = 0;
end;

internal procedure ImportDisputeData(DisputeToken: JsonToken)
var
Dispute: Record "Shpfy Dispute";
RecordRef: RecordRef;
DisputeStatus: Enum "Shpfy Dispute Status";
Id: BigInteger;
begin
Id := JsonHelper.GetValueAsBigInteger(DisputeToken, 'id');
DisputeStatus := ConvertToDisputeStatus(JsonHelper.GetValueAsText(DisputeToken, 'status'));

Clear(Dispute);
if not Dispute.Get(Id) then begin
RecordRef.Open(Database::"Shpfy Dispute");
RecordRef.Init();
JsonHelper.GetValueIntoField(DisputeToken, 'order_id', RecordRef, Dispute.FieldNo("Source Order Id"));
JsonHelper.GetValueIntoField(DisputeToken, 'currency', RecordRef, Dispute.FieldNo(Currency));
JsonHelper.GetValueIntoField(DisputeToken, 'amount', RecordRef, Dispute.FieldNo(Amount));
JsonHelper.GetValueIntoField(DisputeToken, 'evidence_due_by', RecordRef, Dispute.FieldNo("Evidence Due By"));
JsonHelper.GetValueIntoField(DisputeToken, 'evidence_sent_on', RecordRef, Dispute.FieldNo("Evidence Sent On"));
JsonHelper.GetValueIntoField(DisputeToken, 'finalized_on', RecordRef, Dispute.FieldNo("Finalized On"));
RecordRef.SetTable(Dispute);
RecordRef.Close();
Dispute.Id := Id;
Dispute.Status := ConvertToDisputeStatus(JsonHelper.GetValueAsText(DisputeToken, 'status'));
Dispute.Type := ConvertToDisputeType(JsonHelper.GetValueAsText(DisputeToken, 'type'));
Dispute.Reason := ConvertToDisputeReason(JsonHelper.GetValueAsText(DisputeToken, 'reason'));
Dispute.Insert();
end else begin
Dispute.Status := ConvertToDisputeStatus(JsonHelper.GetValueAsText(DisputeToken, 'status'));
Dispute."Evidence Sent On" := JsonHelper.GetValueAsDateTime(DisputeToken, 'evidence_due_by');
Dispute."Finalized On" := JsonHelper.GetValueAsDateTime(DisputeToken, 'finalized_on');

Dispute.Modify();
end;
end;

local procedure ConvertToDisputeStatus(Value: Text): Enum "Shpfy Dispute Status"
begin
Value := CommunicationMgt.ConvertToCleanOptionValue(Value);
if Enum::"Shpfy Dispute Status".Names().Contains(Value) then
exit(Enum::"Shpfy Dispute Status".FromInteger(Enum::"Shpfy Dispute Status".Ordinals().Get(Enum::"Shpfy Dispute Status".Names().IndexOf(Value))))
else
exit(Enum::"Shpfy Dispute Status"::Unknown);
end;

local procedure ConvertToDisputeType(Value: Text): Enum "Shpfy Dispute Type"
begin
Value := CommunicationMgt.ConvertToCleanOptionValue(Value);
if Enum::"Shpfy Dispute Type".Names().Contains(Value) then
exit(Enum::"Shpfy Dispute Type".FromInteger(Enum::"Shpfy Dispute Type".Ordinals().Get(Enum::"Shpfy Dispute Type".Names().IndexOf(Value))))
else
exit(Enum::"Shpfy Dispute Type"::Unknown);
end;

local procedure ConvertToDisputeReason(Value: Text): Enum "Shpfy Dispute Reason"
begin
Value := CommunicationMgt.ConvertToCleanOptionValue(Value);
if Enum::"Shpfy Dispute Status".Names().Contains(Value) then
exit(Enum::"Shpfy Dispute Reason".FromInteger(Enum::"Shpfy Dispute Reason".Ordinals().Get(Enum::"Shpfy Dispute Reason".Names().IndexOf(Value))))
else
exit(Enum::"Shpfy Dispute Reason"::Unknown);
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
namespace Microsoft.Integration.Shopify;

enum 30153 "Shpfy Dispute Reason"
{
/// <summary>
/// Enum Shpfy Dispute Reason (ID 30152).
/// </summary>
///
Caption = 'Shopify Dispute Reason';
Extensible = true;


value(0; Unknown)
{
Caption = 'Unknown';
}
value(1; bank_not_process)
{
Caption = 'Bank Not Process';
}
value(2; credit_not_processed)
{
Caption = 'Credit Not Processed';
}
value(3; customer_initiated)
{
Caption = 'Customer Initiated';
}
value(4; debit_not_authorized)
{
Caption = 'Debit Not Authorized';
}
value(5; duplicate)
{
Caption = 'Duplicate';
}
value(6; fraudulent)
{
Caption = 'Fraudulent';
}
value(7; general)
{
Caption = 'General';
}
value(8; incorrect_account_details)
{
Caption = 'Incorrect Account Details';
}
value(9; insufficient_funds)
{
Caption = 'Insufficient Funds';
}
value(10; product_not_received)
{
Caption = 'Product Not Received';
}
value(11; product_unacceptable)
{
Caption = 'Product Unacceptable';
}
value(12; subscription_canceled)
{
Caption = 'Subscription Canceled';
}
value(13; unrecognized)
{
Caption = 'Unrecognized';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
namespace Microsoft.Integration.Shopify;
enum 30154 "Shpfy Dispute Status"
{
/// <summary>
/// Enum Shpfy Dispute Status (ID 30150).
/// </summary>

Caption = 'Shopify Dispute Status';

value(0; Unknown)
{
Caption = ' ';
}
/// <summary>
/// The dispute has been open and needs an evidence submission.
/// </summary>
value(1; "Needs Response")
{
Caption = 'Needs Response';
}
/// <summary>
/// The evidence has been submitted and is being reviewed by the cardholder's bank.
/// </summary>
value(2; "Under Review")
{
Caption = 'Under Review';
}
/// <summary>
/// The merchant refunded the inquiry amount.
/// </summary>
value(3; "Charge Refunded")
{
Caption = 'Charge Refunded';
}
/// <summary>
/// The merchant has accepted the dispute as being valid.
/// </summary>
value(4; "Accepted")
{
Caption = 'Accepted';
}
/// <summary>
/// The cardholder's bank reached a final decision in the merchant's favor.
/// </summary>
value(5; "Won")
{
Caption = 'Won';
}
/// <summary>
/// The cardholder's bank reached a final decision in the buyer's favor.
/// </summary>
value(6; "Lost")
{
Caption = 'Lost';
}
}
23 changes: 23 additions & 0 deletions Apps/W1/Shopify/app/src/Payments/Enums/ShpfyDisputeType.Enum.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Microsoft.Integration.Shopify;

enum 30155 "Shpfy Dispute Type"
{
/// <summary>
/// Enum Shpfy Dispute Type (ID 30151).
/// </summary>
///
Caption = 'Shopify Dispute Type';

value(0; Unknown)
{
Caption = ' ';
}
value(1; Inquiry)
{
Caption = 'Inquiry';
}
value(2; Chargeback)
{
Caption = 'Chargeback';
}
}
Loading
Loading