Skip to content

Commit 2dcff38

Browse files
Shiko1stleotsarev
authored andcommitted
- anonymous access is allowed to handling endpoints;
- in case of missed payment information trying to update last proposed payment; - payment handling refactored.
1 parent 02abe24 commit 2dcff38

File tree

5 files changed

+125
-81
lines changed

5 files changed

+125
-81
lines changed

src/JoinRpg.DataModel/Finances/FinanceOperation.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
8181
// Checking money value
8282
switch (OperationType)
8383
{
84+
#pragma warning disable CS0612 // Type or member is obsolete
8485
case FinanceOperationType.FeeChange:
86+
#pragma warning restore CS0612 // Type or member is obsolete
8587
if (MoneyAmount != 0)
8688
{
8789
yield return new ValidationResult($"Operation type {OperationType} must not have money amount", new[] { nameof(MoneyAmount) });

src/JoinRpg.Portal/Controllers/Money/PaymentsController.cs

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
using System;
2-
using System.Security.Policy;
32
using System.Threading.Tasks;
4-
using JetBrains.Annotations;
5-
using JoinRpg.Data.Interfaces;
63
using JoinRpg.Interfaces;
74
using JoinRpg.Services.Interfaces;
85
using JoinRpg.Web.Models;
@@ -24,7 +21,7 @@ public PaymentsController(
2421
_payments = payments;
2522
}
2623

27-
private string GetClaimUrl(int projectId, int claimId)
24+
private string? GetClaimUrl(int projectId, int claimId)
2825
=> Url.Action("Edit", "Claim", new { projectId, claimId });
2926

3027
/// <summary>
@@ -89,65 +86,72 @@ public async Task<ActionResult> ClaimPayment(PaymentViewModel data)
8986
}
9087
}
9188

92-
private async Task<ActionResult> HandleClaimPaymentRedirect(int projectId, int claimId, string orderId, string description, string errorMessage)
89+
private async Task<ActionResult> HandleClaimPaymentRedirect(int projectId, int claimId, string orderId, string? description, string errorMessage)
9390
{
94-
if (int.TryParse(orderId, out var financeOperationId))
91+
var financeOperationId = 0;
92+
try
9593
{
96-
try
94+
if (int.TryParse(orderId, out financeOperationId))
9795
{
9896
await _payments.UpdateClaimPaymentAsync(projectId, claimId, financeOperationId);
99-
return RedirectToAction("Edit", "Claim", new { projectId, claimId });
10097
}
101-
catch (Exception e)
98+
else
10299
{
103-
return Error(
104-
new ErrorViewModel
105-
{
106-
Message = $"{errorMessage} {financeOperationId}",
107-
Description = e.Message,
108-
Data = e,
109-
ReturnLink = GetClaimUrl(projectId, claimId),
110-
ReturnText = "Вернуться к заявке",
111-
});
100+
await _payments.UpdateLastClaimPaymentAsync(projectId, claimId);
112101
}
113-
}
114102

115-
return Error(
116-
new ErrorViewModel
117-
{
118-
Message = $"Неверный идентификатор платежа: {orderId}",
119-
ReturnLink = GetClaimUrl(projectId, claimId),
120-
ReturnText = "Вернуться к заявке",
121-
});
103+
// TODO: In case of invalid payment redirect to special page
104+
return RedirectToAction("Edit", "Claim", new { projectId, claimId });
105+
}
106+
catch (Exception e)
107+
{
108+
string foText = financeOperationId > 0
109+
? financeOperationId.ToString()
110+
: "unknown finance operation";
111+
return Error(
112+
new ErrorViewModel
113+
{
114+
Message = $"{errorMessage} {foText}",
115+
Description = e.Message,
116+
Data = e,
117+
ReturnLink = GetClaimUrl(projectId, claimId),
118+
ReturnText = "Вернуться к заявке",
119+
});
120+
}
122121
}
123122

123+
//TODO: why we are losing cookies here? It's ok, because we don't do anything insecure here, but still...
124+
// What we are doing here?
125+
// 1. ask bank about status of orderId
126+
// 2. Update status if required
127+
// 3. Redirect to claim
124128
[HttpGet]
125-
[Authorize]
129+
[AllowAnonymous]
126130
[ActionName(nameof(ClaimPaymentSuccess))]
127131
public async Task<ActionResult> ClaimPaymentSuccessGet(int projectId, int claimId, string orderId)
128132
=> await HandleClaimPaymentRedirect(projectId, claimId, orderId, "",
129133
"Ошибка обработки успешного платежа");
130134

131135

132136
[HttpPost]
133-
[Authorize]
137+
[AllowAnonymous] //TODO see above
134138
public async Task<ActionResult> ClaimPaymentSuccess(int projectId, int claimId, string orderId)
135139
=> await HandleClaimPaymentRedirect(projectId, claimId, orderId, "",
136140
"Ошибка обработки успешного платежа");
137141

138142

139143
[HttpGet]
140-
[Authorize]
144+
[AllowAnonymous] //TODO see above
141145
[ActionName(nameof(ClaimPaymentFail))]
142146
public async Task<ActionResult> ClaimPaymentFailGet(int projectId, int claimId, string orderId,
143-
[CanBeNull] string description)
147+
string? description)
144148
=> await HandleClaimPaymentRedirect(projectId, claimId, orderId, description,
145149
"Ошибка обработки неудавшегося платежа");
146150

147151
[HttpPost]
148-
[Authorize]
152+
[AllowAnonymous] //TODO see above
149153
public async Task<ActionResult> ClaimPaymentFail(int projectId, int claimId, string orderId,
150-
[CanBeNull] string description)
154+
string? description)
151155
=> await HandleClaimPaymentRedirect(projectId, claimId, orderId, description,
152156
"Ошибка обработки неудавшегося платежа");
153157

src/JoinRpg.Services.Impl/PaymentsService.cs

Lines changed: 78 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Data.Entity;
34
using System.Linq;
45
using System.Threading.Tasks;
56
using JoinRpg.Data.Write.Interfaces;
@@ -40,7 +41,6 @@ private ApiConfiguration GetApiConfiguration(int projectId, int claimId)
4041
ApiEndpoint = _bankSecrets.ApiEndpoint,
4142
ApiDebugEndpoint = _bankSecrets.ApiDebugEndpoint,
4243
MerchantId = _bankSecrets.MerchantId,
43-
MerchantIdFastPayments = _bankSecrets.MerchantIdFastPayments,
4444
ApiKey = _bankSecrets.ApiKey,
4545
ApiDebugKey = _bankSecrets.ApiDebugKey,
4646
DefaultSuccessUrl = _uriService.Get(new PaymentSuccessUrl(projectId, claimId)),
@@ -155,7 +155,7 @@ private async Task<Comment> AddPaymentCommentAsync(
155155
claim,
156156
CurrentUserId,
157157
Now,
158-
request.CommentText ?? "",
158+
request.CommentText,
159159
true,
160160
null);
161161
comment.Finance = new FinanceOperation
@@ -204,6 +204,22 @@ private async Task<FinanceOperation> LoadFinanceOperationAsync(int projectId, in
204204
return fo;
205205
}
206206

207+
private async Task<FinanceOperation?> LoadLastUnapprovedFinanceOperationAsync(int projectId, int claimId)
208+
{
209+
return await (from fo in UnitOfWork.GetDbSet<FinanceOperation>()
210+
join pt in UnitOfWork.GetDbSet<PaymentType>()
211+
on fo.PaymentTypeId equals pt.PaymentTypeId
212+
where fo.ProjectId == projectId
213+
&& fo.ClaimId == claimId
214+
&& fo.OperationType == FinanceOperationType.Online
215+
&& pt.TypeKind == PaymentTypeKind.Online
216+
&& fo.State == FinanceOperationState.Proposed
217+
select fo)
218+
.Include(fo => fo.PaymentType)
219+
.OrderByDescending(fo => fo.Created)
220+
.FirstOrDefaultAsync();
221+
}
222+
207223
private void UpdateFinanceOperationStatus(FinanceOperation fo, PaymentData paymentData)
208224
{
209225
switch (paymentData.Status)
@@ -241,64 +257,79 @@ private void UpdateFinanceOperationStatus(FinanceOperation fo, PaymentData payme
241257
}
242258
}
243259

244-
/// <inheritdoc />
245-
public async Task UpdateClaimPaymentAsync(int projectId, int claimId, int orderId)
260+
private async Task UpdateClaimPaymentAsync(FinanceOperation fo)
246261
{
247-
var fo = await LoadFinanceOperationAsync(projectId, claimId, orderId);
248-
249-
if (fo.State == FinanceOperationState.Proposed)
262+
if (fo.State != FinanceOperationState.Proposed)
250263
{
251-
var api = GetApi(projectId, claimId);
252-
string orderIdStr = orderId.ToString().PadLeft(10, '0');
264+
return;
265+
}
266+
267+
var api = GetApi(fo.ProjectId, fo.ClaimId);
268+
string orderIdStr = fo.CommentId.ToString().PadLeft(10, '0');
269+
270+
// Asking bank
271+
PaymentInfo paymentInfo = await api.GetPaymentInfoAsync(
272+
PscbPaymentMethod.BankCards,
273+
orderIdStr);
253274

254-
// Asking bank
255-
PaymentInfo paymentInfo = await api.GetPaymentInfoAsync(
256-
PscbPaymentMethod.BankCards,
275+
if (paymentInfo.Status == PaymentInfoQueryStatus.Failure
276+
&& paymentInfo.ErrorCode == ApiErrorCode.UnknownPayment)
277+
{
278+
paymentInfo = await api.GetPaymentInfoAsync(
279+
PscbPaymentMethod.FastPaymentsSystem,
257280
orderIdStr);
281+
}
258282

259-
if (paymentInfo.Status == PaymentInfoQueryStatus.Failure
260-
&& paymentInfo.ErrorCode == ApiErrorCode.UnknownPayment)
283+
// Updating status
284+
if (paymentInfo.Status == PaymentInfoQueryStatus.Success)
285+
{
286+
if (paymentInfo.ErrorCode == ApiErrorCode.UnknownPayment)
261287
{
262-
paymentInfo = await api.GetPaymentInfoAsync(
263-
PscbPaymentMethod.FastPaymentsSystem,
264-
orderIdStr);
288+
fo.State = FinanceOperationState.Declined;
289+
fo.Changed = Now;
265290
}
266-
267-
// Updating status
268-
if (paymentInfo.Status == PaymentInfoQueryStatus.Success)
291+
else if (paymentInfo.ErrorCode == null)
269292
{
270-
if (paymentInfo.ErrorCode == ApiErrorCode.UnknownPayment)
271-
{
272-
fo.State = FinanceOperationState.Declined;
273-
fo.Changed = Now;
274-
}
275-
else if (paymentInfo.ErrorCode == null)
293+
UpdateFinanceOperationStatus(fo, paymentInfo.Payment);
294+
if (fo.State == FinanceOperationState.Approved)
276295
{
277-
UpdateFinanceOperationStatus(fo, paymentInfo.Payment);
278-
if (fo.State == FinanceOperationState.Approved)
279-
{
280-
Claim claim = await GetClaimAsync(projectId, claimId);
281-
claim.UpdateClaimFeeIfRequired(Now);
282-
}
296+
Claim claim = await GetClaimAsync(fo.ProjectId, fo.ClaimId);
297+
claim.UpdateClaimFeeIfRequired(Now);
283298
}
284299
}
285-
else if (paymentInfo.ErrorCode == ApiErrorCode.UnknownPayment)
286-
{
287-
fo.State = FinanceOperationState.Invalid;
288-
fo.Changed = Now;
289-
}
290-
else if (IsCurrentUserAdmin)
291-
{
292-
throw new PaymentException(fo.Project, $"Payment status check failed: {paymentInfo.ErrorDescription}");
293-
}
300+
}
301+
else if (paymentInfo.ErrorCode == ApiErrorCode.UnknownPayment)
302+
{
303+
fo.State = FinanceOperationState.Invalid;
304+
fo.Changed = Now;
305+
}
306+
else if (IsCurrentUserAdmin)
307+
{
308+
throw new PaymentException(
309+
fo.Project,
310+
$"Payment status check failed: {paymentInfo.ErrorDescription}");
311+
}
294312

295-
// Saving if status was updated
296-
if (fo.State != FinanceOperationState.Proposed)
297-
{
298-
await UnitOfWork.SaveChangesAsync();
299-
}
313+
// Saving if status was updated
314+
if (fo.State != FinanceOperationState.Proposed)
315+
{
316+
await UnitOfWork.SaveChangesAsync();
317+
}
318+
319+
// TODO: Probably need to send some notifications?
320+
}
321+
322+
/// <inheritdoc />
323+
public async Task UpdateClaimPaymentAsync(int projectId, int claimId, int orderId)
324+
=> await UpdateClaimPaymentAsync(await LoadFinanceOperationAsync(projectId, claimId, orderId));
300325

301-
// TODO: Probably need to send some notifications?
326+
/// <inheritdoc />
327+
public async Task UpdateLastClaimPaymentAsync(int projectId, int claimId)
328+
{
329+
var fo = await LoadLastUnapprovedFinanceOperationAsync(projectId, claimId);
330+
if (fo is not null)
331+
{
332+
await UpdateClaimPaymentAsync(fo);
302333
}
303334
}
304335

src/JoinRpg.Services.Interfaces/IPaymentsService.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,18 @@ public interface IPaymentsService
123123
Task<ClaimPaymentContext> InitiateClaimPaymentAsync(ClaimPaymentRequest request);
124124

125125
/// <summary>
126-
/// Updates status of the payment (only if it is not already approved)
126+
/// Updates status of a proposed payment
127127
/// </summary>
128128
/// <param name="projectId">Database Id of a project</param>
129129
/// <param name="claimId">Database Id of a claim</param>
130130
/// <param name="orderId">Finance operation Id</param>
131131
Task UpdateClaimPaymentAsync(int projectId, int claimId, int orderId);
132+
133+
/// <summary>
134+
/// Updates status of the last proposed payment
135+
/// </summary>
136+
/// <param name="projectId">Database Id of a project</param>
137+
/// <param name="claimId">Database Id of a claim</param>
138+
Task UpdateLastClaimPaymentAsync(int projectId, int claimId);
132139
}
133140
}

src/JoinRpg.WebPortal.Models/ErrorViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class ErrorViewModel
99

1010
public string Description { get; set; }
1111

12-
public string ReturnLink { get; set; }
12+
public string? ReturnLink { get; set; }
1313

1414
public string ReturnText { get; set; }
1515

0 commit comments

Comments
 (0)