Skip to content

Commit 0de4d8d

Browse files
committed
PayPal Plus: Getting started with payment wall
1 parent d9452de commit 0de4d8d

9 files changed

Lines changed: 192 additions & 28 deletions

File tree

src/Plugins/SmartStore.PayPal/Controllers/PayPalPlusController.cs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq;
3+
using System.Web;
34
using System.Web.Mvc;
45
using SmartStore.PayPal.Models;
56
using SmartStore.PayPal.Services;
@@ -14,9 +15,11 @@ namespace SmartStore.PayPal.Controllers
1415
{
1516
public class PayPalPlusController : PayPalControllerBase<PayPalPlusPaymentSettings>
1617
{
18+
private readonly HttpContextBase _httpContext;
1719
private readonly IPayPalService _payPalService;
1820

1921
public PayPalPlusController(
22+
HttpContextBase httpContext,
2023
IPaymentService paymentService,
2124
IOrderService orderService,
2225
IOrderProcessingService orderProcessingService,
@@ -26,6 +29,7 @@ public PayPalPlusController(
2629
orderService,
2730
orderProcessingService)
2831
{
32+
_httpContext = httpContext;
2933
_payPalService = payPalService;
3034
}
3135

@@ -105,10 +109,12 @@ public ActionResult UpsertExperienceProfile(string profileId)
105109
var settings = Services.Settings.LoadSetting<PayPalPlusPaymentSettings>(storeScope);
106110

107111
var store = Services.StoreService.GetStoreById(storeScope == 0 ? Services.StoreContext.CurrentStore.Id : storeScope);
112+
var session = new PayPalSessionData();
108113

109-
var result = _payPalService.UpsertCheckoutExperience(settings, store, profileId);
110-
if (result != null)
114+
var result = _payPalService.EnsureAccessToken(session, settings);
115+
if (result.Success)
111116
{
117+
result = _payPalService.UpsertCheckoutExperience(settings, session, store, profileId);
112118
if (result.Success && result.Id.HasValue())
113119
{
114120
settings.ExperienceProfileId = result.Id;
@@ -120,6 +126,10 @@ public ActionResult UpsertExperienceProfile(string profileId)
120126
NotifyError(result.ErrorMessage);
121127
}
122128
}
129+
else
130+
{
131+
NotifyError(result.ErrorMessage);
132+
}
123133

124134
return RedirectToAction("ConfigureProvider", "Plugin", new { area = "admin", systemName = PayPalPlusProvider.SystemName });
125135
}
@@ -131,15 +141,67 @@ public ActionResult PaymentInfo()
131141

132142
public ActionResult PaymentWall()
133143
{
144+
var store = Services.StoreContext.CurrentStore;
145+
var customer = Services.WorkContext.CurrentCustomer;
146+
var settings = Services.Settings.LoadSetting<PayPalPlusPaymentSettings>(store.Id);
147+
134148
var model = new PayPalPlusCheckoutModel();
149+
var state = _httpContext.GetCheckoutState();
150+
151+
if (!state.CustomProperties.ContainsKey(PayPalPlusProvider.SystemName))
152+
state.CustomProperties.Add(PayPalPlusProvider.SystemName, new PayPalSessionData());
153+
154+
var session = state.CustomProperties[PayPalPlusProvider.SystemName] as PayPalSessionData;
155+
156+
model.UseSandbox = settings.UseSandbox;
157+
model.HasPaymentFee = (settings.AdditionalFee > decimal.Zero);
158+
model.LanguageCulture = (Services.WorkContext.WorkingLanguage.LanguageCulture ?? "de_DE").Replace("-", "_");
159+
160+
if (customer.BillingAddress != null && customer.BillingAddress.Country != null)
161+
{
162+
model.BillingAddressCountryCode = customer.BillingAddress.Country.TwoLetterIsoCode;
163+
}
164+
165+
var protocol = (store.SslEnabled ? "https" : "http");
166+
var returnUrl = Url.Action("CheckoutReturn", "PayPalPlus", new { area = Plugin.SystemName }, protocol);
167+
var cancelUrl = Url.Action("CheckoutCancel", "PayPalPlus", new { area = Plugin.SystemName }, protocol);
168+
169+
170+
var result = _payPalService.EnsureAccessToken(session, settings);
171+
if (result.Success)
172+
{
173+
result = _payPalService.UpsertPayment(settings, session, null, returnUrl, cancelUrl);
174+
if (result.Success && result.Json != null)
175+
{
176+
foreach (var link in result.Json.links)
177+
{
178+
if (((string)link.rel).IsCaseInsensitiveEqual("approval_url"))
179+
{
180+
model.ApprovalUrl = link.href;
181+
break;
182+
}
183+
}
184+
}
185+
else if (result.ErrorMessage.HasValue())
186+
{
187+
ModelState.AddModelError("", result.ErrorMessage);
188+
}
189+
}
190+
else if (result.ErrorMessage.HasValue())
191+
{
192+
ModelState.AddModelError("", result.ErrorMessage);
193+
}
135194

136195
return View(model);
137196
}
138197

139-
[HttpPost]
140-
public ActionResult PaymentWall(FormCollection form)
198+
public ActionResult CheckoutReturn()
141199
{
200+
return RedirectToAction("Confirm", "Checkout", new { area = "" });
201+
}
142202

203+
public ActionResult CheckoutCancel()
204+
{
143205
return RedirectToAction("Confirm", "Checkout", new { area = "" });
144206
}
145207
}

src/Plugins/SmartStore.PayPal/Models/PayPalPlusCheckoutModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,10 @@ namespace SmartStore.PayPal.Models
44
{
55
public class PayPalPlusCheckoutModel : ModelBase
66
{
7+
public bool UseSandbox { get; set; }
8+
public bool HasPaymentFee { get; set; }
9+
public string BillingAddressCountryCode { get; set; }
10+
public string LanguageCulture { get; set; }
11+
public string ApprovalUrl { get; set; }
712
}
813
}

src/Plugins/SmartStore.PayPal/Services/IPayPalService.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ public interface IPayPalService
1616

1717
PayPalResponse EnsureAccessToken(PayPalSessionData session, PayPalApiSettingsBase settings);
1818

19-
PayPalResponse UpsertCheckoutExperience(PayPalApiSettingsBase settings, Store store, string profileId);
19+
PayPalResponse UpsertCheckoutExperience(PayPalApiSettingsBase settings, PayPalSessionData session, Store store, string profileId);
20+
21+
PayPalResponse UpsertPayment(
22+
PayPalApiSettingsBase settings,
23+
PayPalSessionData session,
24+
string paymentId,
25+
string returnUrl,
26+
string cancelUrl);
2027
}
2128
}

src/Plugins/SmartStore.PayPal/Services/PayPalService.cs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using System.Net;
56
using System.Text;
67
using System.Web;
@@ -263,14 +264,9 @@ public PayPalResponse EnsureAccessToken(PayPalSessionData session, PayPalApiSett
263264
};
264265
}
265266

266-
public PayPalResponse UpsertCheckoutExperience(PayPalApiSettingsBase settings, Store store, string profileId)
267+
public PayPalResponse UpsertCheckoutExperience(PayPalApiSettingsBase settings, PayPalSessionData session, Store store, string profileId)
267268
{
268-
var session = new PayPalSessionData();
269-
var result = EnsureAccessToken(session, settings);
270-
271-
if (!result.Success)
272-
return result;
273-
269+
PayPalResponse result;
274270
var name = store.Name;
275271
var logo = _pictureService.Value.GetPictureById(store.LogoPictureId);
276272
var path = "/v1/payment-experience/web-profiles";
@@ -325,6 +321,52 @@ public PayPalResponse UpsertCheckoutExperience(PayPalApiSettingsBase settings, S
325321

326322
return result;
327323
}
324+
325+
public PayPalResponse UpsertPayment(
326+
PayPalApiSettingsBase settings,
327+
PayPalSessionData session,
328+
string paymentId,
329+
string returnUrl,
330+
string cancelUrl)
331+
{
332+
var path = "/v1/payments/payment";
333+
var data = new Dictionary<string, object>();
334+
var redirectUrls = new Dictionary<string, object>();
335+
336+
if (paymentId.HasValue())
337+
path = string.Concat(path, "/", HttpUtility.UrlPathEncode(paymentId));
338+
339+
data.Add("intent", settings.TransactMode == TransactMode.AuthorizeAndCapture ? "sale" : "authorize");
340+
341+
if (settings.ExperienceProfileId.HasValue())
342+
data.Add("experience_profile_id", settings.ExperienceProfileId);
343+
344+
if (returnUrl.HasValue())
345+
redirectUrls.Add("return_url", returnUrl);
346+
347+
if (cancelUrl.HasValue())
348+
redirectUrls.Add("cancel_url", cancelUrl);
349+
350+
if (redirectUrls.Any())
351+
data.Add("redirect_urls", redirectUrls);
352+
353+
if (paymentId.IsEmpty())
354+
{
355+
// TODO: add line items
356+
}
357+
358+
var result = CallApi(paymentId.HasValue() ? "PATCH" : "POST", path, session.AccessToken, settings, JsonConvert.SerializeObject(data));
359+
360+
if (result.Success)
361+
{
362+
if (result.Json != null)
363+
result.Id = (string)result.Json.id;
364+
else
365+
result.Id = paymentId;
366+
}
367+
368+
return result;
369+
}
328370
}
329371

330372

@@ -341,5 +383,4 @@ public class PayPalSessionData
341383
public string AccessToken { get; set; }
342384
public DateTime TokenExpiration { get; set; }
343385
}
344-
345386
}

src/Plugins/SmartStore.PayPal/Settings/PayPalSettings.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public abstract class PayPalApiSettingsBase : PayPalSettingsBase
4040

4141
public string ClientId { get; set; }
4242
public string Secret { get; set; }
43+
public string ExperienceProfileId { get; set; }
4344
}
4445

4546

@@ -93,8 +94,6 @@ public PayPalExpressPaymentSettings()
9394

9495
public class PayPalPlusPaymentSettings : PayPalApiSettingsBase, ISettings
9596
{
96-
public string ExperienceProfileId { get; set; }
97-
9897
public PayPalPlusPaymentSettings()
9998
{
10099
UseSandbox = true;

src/Plugins/SmartStore.PayPal/SmartStore.PayPal.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@
288288
<Content Include="Views\PayPalPlus\PaymentWall.Mobile.cshtml">
289289
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
290290
</Content>
291+
<Content Include="Views\PayPalPlus\PaymentWallScripting.cshtml">
292+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
293+
</Content>
291294
<None Include="Web References\PayPalSvc\CoreComponentTypes.xsd">
292295
<SubType>Designer</SubType>
293296
</None>

src/Plugins/SmartStore.PayPal/Views/PayPalPlus/PaymentWall.Mobile.cshtml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
Html.AddTitleParts(T("PageTitle.Checkout").Text);
77
//Html.AddCssFileParts(true, Url.Content("~/Plugins/SmartStore.PayPal/Content/smartstore.paypal.css"));
88
}
9+
10+
@Html.Partial("PaymentWallScripting")
11+
912
<div class="page checkout-page">
1013
<div class="page-title">
1114
<h1>@T("Checkout.SelectPaymentMethod")</h1>
1215
</div>
1316
<div class="page-body checkout-data">
17+
<div id="PayPalPlusPaymentWall"></div>
1418
</div>
1519
</div>

src/Plugins/SmartStore.PayPal/Views/PayPalPlus/PaymentWall.cshtml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,38 @@
1010
{
1111
@Html.Action("CheckoutProgress", "Checkout", new { step = CheckoutProgressStep.Payment })
1212
}
13-
<div class="page payment-method-page">
13+
14+
@if (!ViewData.ModelState.IsValid)
15+
{
16+
@Html.ValidationSummary()
17+
}
18+
19+
@Html.Partial("PaymentWallScripting")
20+
21+
<div id="PayPalPlusPage" class="page payment-method-page">
1422
<div class="page-title">
1523
<h1>@T("Checkout.SelectPaymentMethod")</h1>
1624
</div>
1725
<div class="page-body checkout-data">
18-
hello world!
1926
@using (Html.BeginForm())
2027
{
2128
@Html.Widget("checkout_payment_method_top")
2229

23-
24-
30+
<div id="PayPalPlusPaymentWall"></div>
2531

2632
<div class="select-button clearfix">
2733
<a class="btn pull-left" href="@Url.Action("ShippingMethod", "Checkout", new { area = "" })">
2834
<i class="fa fa-caret-left"></i>&nbsp;@T("Common.Back")
2935
</a>
3036

31-
<input type="submit" name="nextstep" id="nextstep" style="display:none" />
37+
@*TODO: reward points*@
3238

33-
<button class="btn btn-warning pull-right payment-method-next-step-button" onclick="$('#nextstep').trigger('click');return false;">
39+
<button id="PayPalPlusNextCheckoutStep" class="btn btn-warning pull-right payment-method-next-step-button hide">
3440
@T("Checkout.NextButton")&nbsp;<i class="fa fa-caret-right"></i>
3541
</button>
3642
</div>
3743

3844
@Html.Widget("checkout_payment_method_bottom")
3945
}
4046
</div>
41-
</div>
42-
43-
<script>
44-
$(function () {
45-
46-
});
47-
</script>
47+
</div>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@using SmartStore.PayPal.Models;
2+
@model PayPalPlusCheckoutModel
3+
<script src="https://www.paypalobjects.com/webstatic/ppplus/ppplus.min.js" type="text/javascript"></script>
4+
<script type="text/javascript">
5+
jQuery(document).ready(function () {
6+
7+
try {
8+
9+
var ppp = PAYPAL.apps.PPP({
10+
"approvalUrl": "@Model.ApprovalUrl",
11+
"placeholder": "PayPalPlusPaymentWall",
12+
"mode": "@(Model.UseSandbox ? "sandbox" : "live")",
13+
"country": "@(Model.BillingAddressCountryCode ?? "DE")",
14+
"buttonLocation": "outside",
15+
"language": "@(Model.LanguageCulture ?? "de_DE")",
16+
"useraction": "continue",
17+
"surcharging": @Model.HasPaymentFee.ToString().ToLower(),
18+
"showLoadingIndicator": true,
19+
"enableContinue": function () {
20+
$('#PayPalPlusNextCheckoutStep').prop('disabled', false);
21+
},
22+
"disableContinue": function () {
23+
$('#PayPalPlusNextCheckoutStep').prop('disabled', true);
24+
},
25+
"onLoad": function () {
26+
$('#PayPalPlusNextCheckoutStep').fadeIn();
27+
}
28+
});
29+
30+
// next button clicked
31+
$('#PayPalPlusNextCheckoutStep').click(function (e) {
32+
e.preventDefault();
33+
PAYPAL.apps.PPP.doCheckout();
34+
return false;
35+
});
36+
37+
}
38+
catch (e) {
39+
alert(e.description);
40+
}
41+
42+
});
43+
</script>

0 commit comments

Comments
 (0)