├── BeerPal ├── favicon.ico ├── Views │ ├── _ViewStart.cshtml │ ├── Home │ │ ├── About.cshtml │ │ ├── Contact.cshtml │ │ └── Index.cshtml │ ├── Account │ │ ├── ExternalLoginFailure.cshtml │ │ ├── ForgotPasswordConfirmation.cshtml │ │ ├── ConfirmEmail.cshtml │ │ ├── ResetPasswordConfirmation.cshtml │ │ ├── SendCode.cshtml │ │ ├── ForgotPassword.cshtml │ │ ├── _ExternalLoginsListPartial.cshtml │ │ ├── VerifyCode.cshtml │ │ ├── ExternalLoginConfirmation.cshtml │ │ ├── Register.cshtml │ │ ├── ResetPassword.cshtml │ │ └── Login.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── Lockout.cshtml │ │ ├── _LoginPartial.cshtml │ │ └── _Layout.cshtml │ ├── ECommerce │ │ ├── ThankYou.cshtml │ │ ├── Cancel.cshtml │ │ ├── Index.cshtml │ │ └── Cart.cshtml │ ├── Subscription │ │ ├── ThankYou.cshtml │ │ ├── Cancel.cshtml │ │ ├── Index.cshtml │ │ └── Purchase.cshtml │ ├── Single │ │ ├── Cancel.cshtml │ │ ├── ThankYou.cshtml │ │ └── Index.cshtml │ ├── Manage │ │ ├── AddPhoneNumber.cshtml │ │ ├── VerifyPhoneNumber.cshtml │ │ ├── SetPassword.cshtml │ │ ├── ChangePassword.cshtml │ │ ├── ManageLogins.cshtml │ │ └── Index.cshtml │ ├── Subscribers │ │ ├── Details.cshtml │ │ └── Index.cshtml │ ├── WebExperienceProfiles │ │ └── Index.cshtml │ ├── BillingPlans │ │ └── Index.cshtml │ ├── Web.config │ ├── Webhooks │ │ └── Index.cshtml │ ├── Sales │ │ └── Index.cshtml │ └── WebhookEvents │ │ └── Index.cshtml ├── Public │ ├── img │ │ ├── avatar.jpg │ │ ├── img01.jpg │ │ ├── img011.jpg │ │ ├── img012.jpg │ │ ├── img02.jpg │ │ ├── logo.jpg │ │ ├── manuk.jpg │ │ ├── team1.png │ │ ├── team2.png │ │ ├── team3.png │ │ ├── img0112.jpg │ │ ├── location.png │ │ ├── pattern.png │ │ ├── product.jpg │ │ ├── beer-glass.png │ │ ├── brewery-back1.jpg │ │ ├── PayPalCheckoutLogo.png │ │ └── BenCull_square_200x200.jpg │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── js │ │ ├── script.js │ │ ├── html5shiv.js │ │ └── respond.min.js │ └── css │ │ ├── wizard.min.css │ │ ├── wizard.css │ │ └── wizard.less ├── Scripts │ ├── _references.js │ ├── respond.min.js │ └── jquery.validate.unobtrusive.min.js ├── Global.asax ├── Migrations │ ├── Configuration.cs │ ├── 201608281029223_AddedSubscriptionToken.cs │ ├── 201610050827335_AddedShippingAndTax.cs │ ├── 201608251011534_AddedBeers.cs │ ├── 201608251011534_AddedBeers.Designer.cs │ ├── 201608251008534_AddedOrders.Designer.cs │ ├── 201610131512574_AddedWebHooks.Designer.cs │ ├── 201608250815248_InitialMigration.Designer.cs │ ├── 201608271501581_AddedSubscriptions.Designer.cs │ ├── 201610050827335_AddedShippingAndTax.Designer.cs │ ├── 201608251112205_RemovedUserFromOrder.Designer.cs │ ├── 201608281029223_AddedSubscriptionToken.Designer.cs │ ├── 201608251112205_RemovedUserFromOrder.cs │ ├── 201608271501581_AddedSubscriptions.cs │ ├── 201610131512574_AddedWebHooks.cs │ ├── 201608251008534_AddedOrders.cs │ └── 201608250815248_InitialMigration.cs ├── compilerconfig.json ├── Entities │ ├── BaseEntity.cs │ ├── Beer.cs │ ├── OrderItem.cs │ ├── Ticket.cs │ ├── Subscription.cs │ ├── WebhookEvent.cs │ └── Order.cs ├── Startup.cs ├── App_Start │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ ├── BundleConfig.cs │ ├── Startup.Auth.cs │ └── IdentityConfig.cs ├── Models │ ├── Subscription │ │ ├── IndexVm.cs │ │ ├── PurchaseVm.cs │ │ └── Plan.cs │ ├── ECommerce │ │ ├── Cart.cs │ │ ├── CartItem.cs │ │ └── IndexVm.cs │ ├── Home │ │ └── IndexVm.cs │ ├── Single │ │ └── IndexVm.cs │ ├── IdentityModels.cs │ ├── ManageViewModels.cs │ └── AccountViewModels.cs ├── Global.asax.cs ├── Controllers │ ├── HomeController.cs │ ├── SalesController.cs │ ├── WebExperienceProfilesController.cs │ ├── SubscribersController.cs │ ├── WebhookEventsController.cs │ ├── WebhooksController.cs │ ├── SubscriptionController.cs │ └── SingleController.cs ├── compilerconfig.json.defaults ├── Web.Debug.config ├── Web.Release.config ├── Properties │ └── AssemblyInfo.cs ├── Extras │ └── SimulatableWebhook.cs ├── packages.config ├── Project_Readme.html └── Web.config ├── .gitattributes ├── BeerPal.sln └── .gitignore /BeerPal/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/favicon.ico -------------------------------------------------------------------------------- /BeerPal/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /BeerPal/Public/img/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/avatar.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/img01.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/img011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/img011.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/img012.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/img012.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/img02.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/logo.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/manuk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/manuk.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/team1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/team1.png -------------------------------------------------------------------------------- /BeerPal/Public/img/team2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/team2.png -------------------------------------------------------------------------------- /BeerPal/Public/img/team3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/team3.png -------------------------------------------------------------------------------- /BeerPal/Public/img/img0112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/img0112.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/location.png -------------------------------------------------------------------------------- /BeerPal/Public/img/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/pattern.png -------------------------------------------------------------------------------- /BeerPal/Public/img/product.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/product.jpg -------------------------------------------------------------------------------- /BeerPal/Scripts/_references.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Scripts/_references.js -------------------------------------------------------------------------------- /BeerPal/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="BeerPal.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /BeerPal/Public/img/beer-glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/beer-glass.png -------------------------------------------------------------------------------- /BeerPal/Migrations/Configuration.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Migrations/Configuration.cs -------------------------------------------------------------------------------- /BeerPal/Public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /BeerPal/Public/img/brewery-back1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/brewery-back1.jpg -------------------------------------------------------------------------------- /BeerPal/Public/img/PayPalCheckoutLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/PayPalCheckoutLogo.png -------------------------------------------------------------------------------- /BeerPal/Public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /BeerPal/Public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /BeerPal/Public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /BeerPal/Public/img/BenCull_square_200x200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/img/BenCull_square_200x200.jpg -------------------------------------------------------------------------------- /BeerPal/compilerconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFile": "Public/css/wizard.css", 4 | "inputFile": "Public/css/wizard.less" 5 | } 6 | ] -------------------------------------------------------------------------------- /BeerPal/Public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /BeerPal/Public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /BeerPal/Public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjcull/BeerPal/HEAD/BeerPal/Public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /BeerPal/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "About"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |

Use this area to provide additional information.

8 | -------------------------------------------------------------------------------- /BeerPal/Views/Account/ExternalLoginFailure.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Login Failure"; 3 | } 4 | 5 |
6 |

@ViewBag.Title.

7 |

Unsuccessful login with service.

8 |
9 | -------------------------------------------------------------------------------- /BeerPal/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | 3 | @{ 4 | ViewBag.Title = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | -------------------------------------------------------------------------------- /BeerPal/Entities/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Entities 7 | { 8 | public class BaseEntity 9 | { 10 | public int Id { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /BeerPal/Views/Shared/Lockout.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | 3 | @{ 4 | ViewBag.Title = "Locked Out"; 5 | } 6 | 7 |
8 |

Locked out.

9 |

This account has been locked out, please try again later.

10 |
11 | -------------------------------------------------------------------------------- /BeerPal/Entities/Beer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Entities 7 | { 8 | public class Beer : BaseEntity 9 | { 10 | public string Name { get; set; } 11 | public int Price { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /BeerPal/Views/Account/ForgotPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Forgot Password Confirmation"; 3 | } 4 | 5 |
6 |

@ViewBag.Title.

7 |
8 |
9 |

10 | Please check your email to reset your password. 11 |

12 |
13 | 14 | -------------------------------------------------------------------------------- /BeerPal/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using Owin; 3 | 4 | [assembly: OwinStartupAttribute(typeof(BeerPal.Startup))] 5 | namespace BeerPal 6 | { 7 | public partial class Startup 8 | { 9 | public void Configuration(IAppBuilder app) 10 | { 11 | ConfigureAuth(app); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BeerPal/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace BeerPal 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BeerPal/Views/Account/ConfirmEmail.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Confirm Email"; 3 | } 4 | 5 |

@ViewBag.Title.

6 |
7 |

8 | Thank you for confirming your email. Please @Html.ActionLink("Click here to Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" }) 9 |

10 |
11 | -------------------------------------------------------------------------------- /BeerPal/Models/Subscription/IndexVm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Models.Subscription 7 | { 8 | public class IndexVm 9 | { 10 | public List Plans { get; set; } 11 | 12 | public IndexVm() 13 | { 14 | Plans = new List(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /BeerPal/Models/ECommerce/Cart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Models.ECommerce 7 | { 8 | public class Cart 9 | { 10 | public List CartItems { get; set; } 11 | 12 | public Cart() 13 | { 14 | CartItems = new List(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /BeerPal/Models/ECommerce/CartItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Models.ECommerce 7 | { 8 | public class CartItem 9 | { 10 | public int BeerId { get; set; } 11 | public string Name { get; set; } 12 | public int Price { get; set; } 13 | public int Quantity { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /BeerPal/Views/Account/ResetPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Reset password confirmation"; 3 | } 4 | 5 |
6 |

@ViewBag.Title.

7 |
8 |
9 |

10 | Your password has been reset. Please @Html.ActionLink("click here to log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" }) 11 |

12 |
13 | -------------------------------------------------------------------------------- /BeerPal/Models/ECommerce/IndexVm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using BeerPal.Entities; 6 | 7 | namespace BeerPal.Models.ECommerce 8 | { 9 | public class IndexVm 10 | { 11 | public List Beers { get; set; } 12 | 13 | public IndexVm() 14 | { 15 | Beers = new List(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /BeerPal/Entities/OrderItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Entities 7 | { 8 | public class OrderItem : BaseEntity 9 | { 10 | public int OrderId { get; set; } 11 | public Order Order { get; set; } 12 | 13 | public string Name { get; set; } 14 | public int Price { get; set; } 15 | public int Quantity { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /BeerPal/Entities/Ticket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Entities 7 | { 8 | public class Ticket : BaseEntity 9 | { 10 | public string FirstName { get; set; } 11 | public string LastName { get; set; } 12 | public string Email { get; set; } 13 | public DateTime TourDate { get; set; } 14 | public string PayPalReference { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /BeerPal/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Contact"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P: 11 | 425.555.0100 12 |
13 | 14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com 17 |
-------------------------------------------------------------------------------- /BeerPal/Models/Home/IndexVm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using BeerPal.Entities; 6 | using BeerPal.Models.Subscription; 7 | 8 | namespace BeerPal.Models.Home 9 | { 10 | public class IndexVm 11 | { 12 | public List Beers { get; set; } 13 | public List Plans { get; set; } 14 | 15 | public IndexVm() 16 | { 17 | Beers = new List(); 18 | Plans = new List(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /BeerPal/Models/Subscription/PurchaseVm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Web; 6 | 7 | namespace BeerPal.Models.Subscription 8 | { 9 | public class PurchaseVm 10 | { 11 | [Required] 12 | public string FirstName { get; set; } 13 | [Required] 14 | public string LastName { get; set; } 15 | [Required] 16 | public string Email { get; set; } 17 | 18 | public Plan Plan { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /BeerPal/Migrations/201608281029223_AddedSubscriptionToken.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class AddedSubscriptionToken : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | AddColumn("dbo.Subscriptions", "PayPalAgreementToken", c => c.String()); 11 | } 12 | 13 | public override void Down() 14 | { 15 | DropColumn("dbo.Subscriptions", "PayPalAgreementToken"); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BeerPal/Models/Single/IndexVm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Web; 6 | 7 | namespace BeerPal.Models.Single 8 | { 9 | public class IndexVm 10 | { 11 | public DateTime TourDate { get; set; } 12 | public int Price { get; set; } 13 | 14 | [Required] 15 | public string FirstName { get; set; } 16 | [Required] 17 | public string LastName { get; set; } 18 | [Required] 19 | public string Email { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /BeerPal/Entities/Subscription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Entities 7 | { 8 | public class Subscription : BaseEntity 9 | { 10 | public string FirstName { get; set; } 11 | public string LastName { get; set; } 12 | public string Email { get; set; } 13 | public DateTime StartDate { get; set; } 14 | public string PayPalPlanId { get; set; } 15 | public string PayPalAgreementToken { get; set; } 16 | public string PayPalAgreementId { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /BeerPal/Entities/WebhookEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Entities 7 | { 8 | public class WebhookEvent : BaseEntity 9 | { 10 | public string PayPalWebHookEventId { get; set; } 11 | public DateTime DateCreated { get; set; } 12 | public DateTime DateReceived { get; set; } 13 | public string EventType { get; set; } 14 | public string Summary { get; set; } 15 | public string ResourceType { get; set; } 16 | public string ResourceJson { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /BeerPal/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Optimization; 7 | using System.Web.Routing; 8 | 9 | namespace BeerPal 10 | { 11 | public class MvcApplication : System.Web.HttpApplication 12 | { 13 | protected void Application_Start() 14 | { 15 | AreaRegistration.RegisterAllAreas(); 16 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 17 | RouteConfig.RegisterRoutes(RouteTable.Routes); 18 | BundleConfig.RegisterBundles(BundleTable.Bundles); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BeerPal/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace BeerPal 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | name: "Default", 18 | url: "{controller}/{action}/{id}", 19 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 | ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BeerPal/Entities/Order.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using BeerPal.Models; 6 | 7 | namespace BeerPal.Entities 8 | { 9 | public class Order : BaseEntity 10 | { 11 | public DateTime OrderDate { get; set; } 12 | public int Total { get; set; } 13 | public int Tax { get; set; } 14 | public int Subtotal { get; set; } 15 | public int Shipping { get; set; } 16 | public string PayPalReference { get; set; } 17 | 18 | public ICollection OrderItems { get; set; } 19 | 20 | public Order() 21 | { 22 | OrderItems = new List(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /BeerPal/Views/ECommerce/ThankYou.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Thank You"; 3 | } 4 | 5 |
6 |
7 |
8 |
9 |

// Thank You

10 |
11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |

Thank You

20 |

Your payment was successful. Your beers are on their way!

21 |
22 |
23 |
24 |
25 | 26 | -------------------------------------------------------------------------------- /BeerPal/Views/Subscription/ThankYou.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Thank You"; 3 | } 4 | 5 |
6 |
7 |
8 |
9 |

// Thank You

10 |
11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |

Thank You

20 |

Your payment was successful. Your beers are on their way!

21 |
22 |
23 |
24 |
25 | 26 | -------------------------------------------------------------------------------- /BeerPal/Public/js/script.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | // Contact Maps 4 | $("#maps").gmap3({ 5 | map: { 6 | options: { 7 | center: [-7.866315,110.389574], 8 | zoom: 8, 9 | scrollwheel: false 10 | } 11 | }, 12 | marker:{ 13 | latLng: [-7.866315,110.389574], 14 | options: { 15 | icon: new google.maps.MarkerImage( 16 | "https://dl.dropboxusercontent.com/u/29545616/Preview/location.png", 17 | new google.maps.Size(48, 48, "px", "px") 18 | ) 19 | } 20 | } 21 | }); 22 | 23 | //Slider 24 | $("#slider").carousel({ 25 | interval: 5000 26 | }); 27 | 28 | $("#testi").carousel({ 29 | interval: 4000 30 | }); 31 | 32 | $("#itemsingle").carousel({ 33 | interval: false 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /BeerPal/Views/Single/Cancel.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Payment Cancelled"; 3 | } 4 | 5 | 6 |
7 |
8 |
9 |
10 |

// Wait just a minute

11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |

Sorry, the payment was cancelled

21 |

Your payment was not successful. Please try again.

22 |
23 |
24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /BeerPal/Views/ECommerce/Cancel.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Payment Cancelled"; 3 | } 4 | 5 | 6 |
7 |
8 |
9 |
10 |

// Wait just a minute

11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |

Sorry, the payment was cancelled

21 |

Your payment was not successful. Please try again.

22 |
23 |
24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /BeerPal/Views/Subscription/Cancel.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Payment Cancelled"; 3 | } 4 | 5 | 6 |
7 |
8 |
9 |
10 |

// Wait just a minute

11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |

Sorry, the payment was cancelled

21 |

Your payment was not successful. Please try again.

22 |
23 |
24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201610050827335_AddedShippingAndTax.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class AddedShippingAndTax : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | AddColumn("dbo.Orders", "Tax", c => c.Int(nullable: false)); 11 | AddColumn("dbo.Orders", "Subtotal", c => c.Int(nullable: false)); 12 | AddColumn("dbo.Orders", "Shipping", c => c.Int(nullable: false)); 13 | } 14 | 15 | public override void Down() 16 | { 17 | DropColumn("dbo.Orders", "Shipping"); 18 | DropColumn("dbo.Orders", "Subtotal"); 19 | DropColumn("dbo.Orders", "Tax"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608251011534_AddedBeers.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class AddedBeers : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.Beers", 12 | c => new 13 | { 14 | Id = c.Int(nullable: false, identity: true), 15 | Name = c.String(), 16 | Price = c.Int(nullable: false), 17 | }) 18 | .PrimaryKey(t => t.Id); 19 | 20 | } 21 | 22 | public override void Down() 23 | { 24 | DropTable("dbo.Beers"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BeerPal/Views/Account/SendCode.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.SendCodeViewModel 2 | @{ 3 | ViewBag.Title = "Send"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("SendCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { 9 | @Html.AntiForgeryToken() 10 | @Html.Hidden("rememberMe", @Model.RememberMe) 11 |

Send verification code

12 |
13 |
14 |
15 | Select Two-Factor Authentication Provider: 16 | @Html.DropDownListFor(model => model.SelectedProvider, Model.Providers) 17 | 18 |
19 |
20 | } 21 | 22 | @section Scripts { 23 | @Scripts.Render("~/bundles/jqueryval") 24 | } 25 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608251011534_AddedBeers.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class AddedBeers : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(AddedBeers)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201608251011534_AddedBeers"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608251008534_AddedOrders.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class AddedOrders : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(AddedOrders)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201608251008534_AddedOrders"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201610131512574_AddedWebHooks.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class AddedWebHooks : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(AddedWebHooks)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201610131512574_AddedWebHooks"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608250815248_InitialMigration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class InitialMigration : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(InitialMigration)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201608250815248_InitialMigration"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNet.Identity 2 | @if (Request.IsAuthenticated) 3 | { 4 | using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" })) 5 | { 6 | @Html.AntiForgeryToken() 7 | 8 | 14 | } 15 | } 16 | else 17 | { 18 | 22 | } 23 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608271501581_AddedSubscriptions.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class AddedSubscriptions : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(AddedSubscriptions)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201608271501581_AddedSubscriptions"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201610050827335_AddedShippingAndTax.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class AddedShippingAndTax : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(AddedShippingAndTax)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201610050827335_AddedShippingAndTax"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608251112205_RemovedUserFromOrder.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class RemovedUserFromOrder : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(RemovedUserFromOrder)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201608251112205_RemovedUserFromOrder"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Views/Manage/AddPhoneNumber.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.AddPhoneNumberViewModel 2 | @{ 3 | ViewBag.Title = "Phone Number"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("AddPhoneNumber", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 |

Add a phone number

12 |
13 | @Html.ValidationSummary("", new { @class = "text-danger" }) 14 |
15 | @Html.LabelFor(m => m.Number, new { @class = "col-md-2 control-label" }) 16 |
17 | @Html.TextBoxFor(m => m.Number, new { @class = "form-control" }) 18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 | } 26 | 27 | @section Scripts { 28 | @Scripts.Render("~/bundles/jqueryval") 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608281029223_AddedSubscriptionToken.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace BeerPal.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] 10 | public sealed partial class AddedSubscriptionToken : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(AddedSubscriptionToken)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201608281029223_AddedSubscriptionToken"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Views/Account/ForgotPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.ForgotPasswordViewModel 2 | @{ 3 | ViewBag.Title = "Forgot your password?"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 |

Enter your email.

12 |
13 | @Html.ValidationSummary("", new { @class = "text-danger" }) 14 |
15 | @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) 16 |
17 | @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) 18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 | } 26 | 27 | @section Scripts { 28 | @Scripts.Render("~/bundles/jqueryval") 29 | } 30 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608251112205_RemovedUserFromOrder.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class RemovedUserFromOrder : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | DropForeignKey("dbo.Orders", "ApplicationUser_Id", "dbo.AspNetUsers"); 11 | DropIndex("dbo.Orders", new[] { "ApplicationUser_Id" }); 12 | DropColumn("dbo.Orders", "ApplicationUserId"); 13 | DropColumn("dbo.Orders", "ApplicationUser_Id"); 14 | } 15 | 16 | public override void Down() 17 | { 18 | AddColumn("dbo.Orders", "ApplicationUser_Id", c => c.String(maxLength: 128)); 19 | AddColumn("dbo.Orders", "ApplicationUserId", c => c.Int(nullable: false)); 20 | CreateIndex("dbo.Orders", "ApplicationUser_Id"); 21 | AddForeignKey("dbo.Orders", "ApplicationUser_Id", "dbo.AspNetUsers", "Id"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BeerPal/Views/Single/ThankYou.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Thank You"; 3 | } 4 | 5 |
6 |
7 |
8 |
9 |

// Thank You

10 |
11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |

Thank You

20 |

Your payment was successful. See you at the brewery Tour!

21 |
22 |
23 | 24 | 29 | 30 |
31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /BeerPal.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeerPal", "BeerPal\BeerPal.csproj", "{ABEEBDEE-EDB3-4988-9A3B-C8E09D5DC2DE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {ABEEBDEE-EDB3-4988-9A3B-C8E09D5DC2DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {ABEEBDEE-EDB3-4988-9A3B-C8E09D5DC2DE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {ABEEBDEE-EDB3-4988-9A3B-C8E09D5DC2DE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {ABEEBDEE-EDB3-4988-9A3B-C8E09D5DC2DE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608271501581_AddedSubscriptions.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class AddedSubscriptions : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.Subscriptions", 12 | c => new 13 | { 14 | Id = c.Int(nullable: false, identity: true), 15 | FirstName = c.String(), 16 | LastName = c.String(), 17 | Email = c.String(), 18 | StartDate = c.DateTime(nullable: false), 19 | PayPalPlanId = c.String(), 20 | PayPalAgreementId = c.String(), 21 | }) 22 | .PrimaryKey(t => t.Id); 23 | 24 | } 25 | 26 | public override void Down() 27 | { 28 | DropTable("dbo.Subscriptions"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BeerPal/Views/Manage/VerifyPhoneNumber.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.VerifyPhoneNumberViewModel 2 | @{ 3 | ViewBag.Title = "Verify Phone Number"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("VerifyPhoneNumber", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 | @Html.Hidden("phoneNumber", @Model.PhoneNumber) 12 |

Enter verification code

13 |
@ViewBag.Status
14 |
15 | @Html.ValidationSummary("", new { @class = "text-danger" }) 16 |
17 | @Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" }) 18 |
19 | @Html.TextBoxFor(m => m.Code, new { @class = "form-control" }) 20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 | } 28 | 29 | @section Scripts { 30 | @Scripts.Render("~/bundles/jqueryval") 31 | } 32 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201610131512574_AddedWebHooks.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class AddedWebHooks : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.WebhookEvents", 12 | c => new 13 | { 14 | Id = c.Int(nullable: false, identity: true), 15 | PayPalWebHookEventId = c.String(), 16 | DateCreated = c.DateTime(nullable: false), 17 | DateReceived = c.DateTime(nullable: false), 18 | EventType = c.String(), 19 | Summary = c.String(), 20 | ResourceType = c.String(), 21 | ResourceJson = c.String(), 22 | }) 23 | .PrimaryKey(t => t.Id); 24 | 25 | } 26 | 27 | public override void Down() 28 | { 29 | DropTable("dbo.WebhookEvents"); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BeerPal/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using BeerPal.Models; 7 | using BeerPal.Models.Subscription; 8 | using Microsoft.AspNet.Identity.Owin; 9 | using IndexVm = BeerPal.Models.Home.IndexVm; 10 | 11 | namespace BeerPal.Controllers 12 | { 13 | public class HomeController : Controller 14 | { 15 | private ApplicationDbContext _dbContext => HttpContext.GetOwinContext().Get(); 16 | 17 | public ActionResult Index() 18 | { 19 | var model = new IndexVm() 20 | { 21 | Beers = _dbContext.Beers.ToList(), 22 | Plans = Plan.Plans 23 | }; 24 | 25 | return View(model); 26 | } 27 | 28 | public ActionResult About() 29 | { 30 | ViewBag.Message = "Your application description page."; 31 | 32 | return View(); 33 | } 34 | 35 | public ActionResult Contact() 36 | { 37 | ViewBag.Message = "Your contact page."; 38 | 39 | return View(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /BeerPal/compilerconfig.json.defaults: -------------------------------------------------------------------------------- 1 | { 2 | "compilers": { 3 | "less": { 4 | "autoPrefix": "", 5 | "cssComb": "none", 6 | "ieCompat": true, 7 | "strictMath": false, 8 | "strictUnits": false, 9 | "relativeUrls": true, 10 | "rootPath": "", 11 | "sourceMapRoot": "", 12 | "sourceMapBasePath": "", 13 | "sourceMap": false 14 | }, 15 | "sass": { 16 | "includePath": "", 17 | "indentType": "space", 18 | "indentWidth": 2, 19 | "outputStyle": "nested", 20 | "Precision": 5, 21 | "relativeUrls": true, 22 | "sourceMapRoot": "", 23 | "sourceMap": false 24 | }, 25 | "stylus": { 26 | "sourceMap": false 27 | }, 28 | "babel": { 29 | "sourceMap": false 30 | }, 31 | "coffeescript": { 32 | "bare": false, 33 | "runtimeMode": "node", 34 | "sourceMap": false 35 | } 36 | }, 37 | "minifiers": { 38 | "css": { 39 | "enabled": true, 40 | "termSemicolons": true, 41 | "gzip": false 42 | }, 43 | "javascript": { 44 | "enabled": true, 45 | "termSemicolons": true, 46 | "gzip": false 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /BeerPal/Views/Account/_ExternalLoginsListPartial.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.ExternalLoginListViewModel 2 | @using Microsoft.Owin.Security 3 | 4 |

Use another service to log in.

5 |
6 | @{ 7 | var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes(); 8 | if (loginProviders.Count() == 0) { 9 |
10 |

11 | There are no external authentication services configured. See this article 12 | for details on setting up this ASP.NET application to support logging in via external services. 13 |

14 |
15 | } 16 | else { 17 | using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl })) { 18 | @Html.AntiForgeryToken() 19 |
20 |

21 | @foreach (AuthenticationDescription p in loginProviders) { 22 | 23 | } 24 |

25 |
26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /BeerPal/Controllers/SalesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using PayPal.Api; 7 | 8 | namespace BeerPal.Controllers 9 | { 10 | public class SalesController : Controller 11 | { 12 | public ActionResult Index() 13 | { 14 | var apiContext = GetApiContext(); 15 | 16 | var sales = Payment.List(apiContext); 17 | 18 | return View(sales); 19 | } 20 | 21 | public ActionResult Refund(string saleId) 22 | { 23 | var apiContext = GetApiContext(); 24 | 25 | var sale = new Sale() 26 | { 27 | id = saleId 28 | }; 29 | 30 | // A refund with no details refunds the entire amount. 31 | var refund = sale.Refund(apiContext, new Refund()); 32 | 33 | return RedirectToAction("Index"); 34 | } 35 | 36 | private APIContext GetApiContext() 37 | { 38 | // Authenticate with PayPal 39 | var config = ConfigManager.Instance.GetProperties(); 40 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 41 | var apiContext = new APIContext(accessToken); 42 | return apiContext; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /BeerPal/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /BeerPal/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace BeerPal 5 | { 6 | public class BundleConfig 7 | { 8 | // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 15 | "~/Scripts/jquery.validate*")); 16 | 17 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 18 | // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. 19 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 20 | "~/Scripts/modernizr-*")); 21 | 22 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 23 | "~/Scripts/bootstrap.js", 24 | "~/Scripts/respond.js")); 25 | 26 | bundles.Add(new StyleBundle("~/Content/css").Include( 27 | "~/Content/bootstrap.css", 28 | "~/Content/site.css")); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BeerPal/Views/Subscribers/Details.cshtml: -------------------------------------------------------------------------------- 1 | @using Newtonsoft.Json 2 | @model PayPal.Api.Agreement 3 | 4 | @{ 5 | ViewBag.Title = "Subscription Details"; 6 | } 7 | 8 |
9 |
10 |
11 |
12 |

// Subscription Details

13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 | Suspend 23 | Re-activate 24 | Cancel 25 |
26 |
27 |
28 |
29 |
@JsonConvert.SerializeObject(Model, Formatting.Indented)
30 |
31 |
32 |
33 |
-------------------------------------------------------------------------------- /BeerPal/Views/Account/VerifyCode.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.VerifyCodeViewModel 2 | @{ 3 | ViewBag.Title = "Verify"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("VerifyCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { 9 | @Html.AntiForgeryToken() 10 | @Html.Hidden("provider", @Model.Provider) 11 | @Html.Hidden("rememberMe", @Model.RememberMe) 12 |

Enter verification code

13 |
14 | @Html.ValidationSummary("", new { @class = "text-danger" }) 15 |
16 | @Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" }) 17 |
18 | @Html.TextBoxFor(m => m.Code, new { @class = "form-control" }) 19 |
20 |
21 |
22 |
23 |
24 | @Html.CheckBoxFor(m => m.RememberBrowser) 25 | @Html.LabelFor(m => m.RememberBrowser) 26 |
27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 | } 35 | 36 | @section Scripts { 37 | @Scripts.Render("~/bundles/jqueryval") 38 | } 39 | -------------------------------------------------------------------------------- /BeerPal/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /BeerPal/Public/css/wizard.min.css: -------------------------------------------------------------------------------- 1 | .nav-pills.nav-wizard>li{position:relative;overflow:visible;border-right:15px solid transparent;border-left:15px solid transparent;}.nav-pills.nav-wizard>li+li{margin-left:0;}.nav-pills.nav-wizard>li:first-child{border-left:0;}.nav-pills.nav-wizard>li:first-child a{border-radius:5px 0 0 5px;}.nav-pills.nav-wizard>li:last-child{border-right:0;}.nav-pills.nav-wizard>li:last-child a{border-radius:0 5px 5px 0;}.nav-pills.nav-wizard>li a{border-radius:0;background-color:#eee;}.nav-pills.nav-wizard>li:not(:last-child) a:after{position:absolute;content:"";top:0;right:-20px;width:0;height:0;border-style:solid;border-width:20px 0 20px 20px;border-color:transparent transparent transparent #eee;z-index:150;}.nav-pills.nav-wizard>li:not(:first-child) a:before{position:absolute;content:"";top:0;left:-20px;width:0;height:0;border-style:solid;border-width:20px 0 20px 20px;border-color:#eee #eee #eee transparent;z-index:150;}.nav-pills.nav-wizard>li:hover:not(:last-child) a:after{border-color:transparent transparent transparent #aaa;}.nav-pills.nav-wizard>li:hover:not(:first-child) a:before{border-color:#aaa #aaa #aaa transparent;}.nav-pills.nav-wizard>li:hover a{background-color:#aaa;color:#fff;}.nav-pills.nav-wizard>li.active:not(:last-child) a:after{border-color:transparent transparent transparent #428bca;}.nav-pills.nav-wizard>li.active:not(:first-child) a:before{border-color:#428bca #428bca #428bca transparent;}.nav-pills.nav-wizard>li.active a{background-color:#428bca;} -------------------------------------------------------------------------------- /BeerPal/Views/Manage/SetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.SetPasswordViewModel 2 | @{ 3 | ViewBag.Title = "Create Password"; 4 | } 5 | 6 |

@ViewBag.Title.

7 |

8 | You do not have a local username/password for this site. Add a local 9 | account so you can log in without an external login. 10 |

11 | 12 | @using (Html.BeginForm("SetPassword", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 13 | { 14 | @Html.AntiForgeryToken() 15 | 16 |

Create Local Login

17 |
18 | @Html.ValidationSummary("", new { @class = "text-danger" }) 19 |
20 | @Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" }) 21 |
22 | @Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" }) 23 |
24 |
25 |
26 | @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) 27 |
28 | @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) 29 |
30 |
31 |
32 |
33 | 34 |
35 |
36 | } 37 | @section Scripts { 38 | @Scripts.Render("~/bundles/jqueryval") 39 | } -------------------------------------------------------------------------------- /BeerPal/Views/Account/ExternalLoginConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.ExternalLoginConfirmationViewModel 2 | @{ 3 | ViewBag.Title = "Register"; 4 | } 5 |

@ViewBag.Title.

6 |

Associate your @ViewBag.LoginProvider account.

7 | 8 | @using (Html.BeginForm("ExternalLoginConfirmation", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 | 12 |

Association Form

13 |
14 | @Html.ValidationSummary(true, "", new { @class = "text-danger" }) 15 |

16 | You've successfully authenticated with @ViewBag.LoginProvider. 17 | Please enter a user name for this site below and click the Register button to finish 18 | logging in. 19 |

20 |
21 | @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) 22 |
23 | @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) 24 | @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" }) 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 | } 33 | 34 | @section Scripts { 35 | @Scripts.Render("~/bundles/jqueryval") 36 | } 37 | -------------------------------------------------------------------------------- /BeerPal/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("BeerPal")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("BeerPal")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0b72fd66-c1b8-4500-bf77-dc4a1b3edffa")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /BeerPal/Views/Account/Register.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.RegisterViewModel 2 | @{ 3 | ViewBag.Title = "Register"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 |

Create a new account.

12 |
13 | @Html.ValidationSummary("", new { @class = "text-danger" }) 14 |
15 | @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) 16 |
17 | @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) 18 |
19 |
20 |
21 | @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) 22 |
23 | @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) 24 |
25 |
26 |
27 | @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) 28 |
29 | @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | } 38 | 39 | @section Scripts { 40 | @Scripts.Render("~/bundles/jqueryval") 41 | } 42 | -------------------------------------------------------------------------------- /BeerPal/Extras/SimulatableWebhook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using Newtonsoft.Json; 6 | using PayPal.Api; 7 | 8 | namespace BeerPal.Extras 9 | { 10 | public class SimulatableWebhook : Webhook 11 | { 12 | public WebhookEvent SimulateEvent(string eventType, string webhookId) 13 | { 14 | var apiContext = GetApiContext(); 15 | 16 | if (apiContext.HTTPHeaders == null) 17 | apiContext.HTTPHeaders = new Dictionary(); 18 | apiContext.HTTPHeaders["Content-Type"] = "application/json"; 19 | apiContext.SdkVersion = new SDKVersion(); 20 | 21 | string resource = "v1/notifications/simulate-event"; 22 | 23 | var data = new 24 | { 25 | webhook_id = webhookId, 26 | event_type = eventType 27 | }; 28 | 29 | var webhookEvent = PayPalResource.ConfigureAndExecute(apiContext, PayPalResource.HttpMethod.POST, resource, JsonConvert.SerializeObject(data), "", true); 30 | 31 | return webhookEvent; 32 | } 33 | 34 | private APIContext GetApiContext() 35 | { 36 | // Authenticate with PayPal 37 | var config = ConfigManager.Instance.GetProperties(); 38 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 39 | var apiContext = new APIContext(accessToken); 40 | return apiContext; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /BeerPal/Views/Manage/ChangePassword.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.ChangePasswordViewModel 2 | @{ 3 | ViewBag.Title = "Change Password"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("ChangePassword", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 |

Change Password Form

12 |
13 | @Html.ValidationSummary("", new { @class = "text-danger" }) 14 |
15 | @Html.LabelFor(m => m.OldPassword, new { @class = "col-md-2 control-label" }) 16 |
17 | @Html.PasswordFor(m => m.OldPassword, new { @class = "form-control" }) 18 |
19 |
20 |
21 | @Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" }) 22 |
23 | @Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" }) 24 |
25 |
26 |
27 | @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) 28 |
29 | @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | } 38 | @section Scripts { 39 | @Scripts.Render("~/bundles/jqueryval") 40 | } -------------------------------------------------------------------------------- /BeerPal/Views/Account/ResetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.ResetPasswordViewModel 2 | @{ 3 | ViewBag.Title = "Reset password"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 | @using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 |

Reset your password.

12 |
13 | @Html.ValidationSummary("", new { @class = "text-danger" }) 14 | @Html.HiddenFor(model => model.Code) 15 |
16 | @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) 17 |
18 | @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) 19 |
20 |
21 |
22 | @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) 23 |
24 | @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) 25 |
26 |
27 |
28 | @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) 29 |
30 | @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) 31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 | } 39 | 40 | @section Scripts { 41 | @Scripts.Render("~/bundles/jqueryval") 42 | } 43 | -------------------------------------------------------------------------------- /BeerPal/Controllers/WebExperienceProfilesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using PayPal.Api; 7 | 8 | namespace BeerPal.Controllers 9 | { 10 | public class WebExperienceProfilesController : Controller 11 | { 12 | public ActionResult Index() 13 | { 14 | var apiContext = GetApiContext(); 15 | 16 | var list = WebProfile.GetList(apiContext); 17 | 18 | if (!list.Any()) 19 | { 20 | SeedWebProfiles(apiContext); 21 | list = WebProfile.GetList(apiContext); 22 | } 23 | 24 | return View(list); 25 | } 26 | 27 | /// 28 | /// Create the default web experience profiles for this example website 29 | /// 30 | private void SeedWebProfiles(APIContext apiContext) 31 | { 32 | var digitalGoods = new WebProfile() 33 | { 34 | name = "DigitalGoods", 35 | input_fields = new InputFields() 36 | { 37 | no_shipping = 1 38 | } 39 | }; 40 | WebProfile.Create(apiContext, digitalGoods); 41 | } 42 | 43 | private APIContext GetApiContext() 44 | { 45 | // Authenticate with PayPal 46 | var config = ConfigManager.Instance.GetProperties(); 47 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 48 | var apiContext = new APIContext(accessToken); 49 | return apiContext; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /BeerPal/Views/WebExperienceProfiles/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using Newtonsoft.Json 2 | @model PayPal.Api.WebProfileList 3 | 4 | @{ 5 | ViewBag.Title = "Web Experience Profiles"; 6 | } 7 | 8 |
9 |
10 |
11 |
12 |

// Web Experience Profiles

13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | @foreach (var profile in Model) 35 | { 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | } 44 | 45 |
IDNameFlow ConfigInput FieldsPresentation
@profile.id@profile.name@JsonConvert.SerializeObject(profile.flow_config)@JsonConvert.SerializeObject(profile.input_fields)@JsonConvert.SerializeObject(profile.presentation)
46 |
47 |
48 |
49 |
50 | -------------------------------------------------------------------------------- /BeerPal/Views/Subscribers/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using BeerPal.Models.Subscription 2 | @model List 3 | 4 | @{ 5 | ViewBag.Title = "Subscribers"; 6 | } 7 | 8 |
9 |
10 |
11 |
12 |

// Subscribers

13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | @foreach (var item in Model) 33 | { 34 | 35 | 36 | 37 | 38 | 41 | 42 | } 43 | 44 |
IDCreated DatePlan
@item.PayPalAgreementId@item.StartDate@(Plan.Plans.FirstOrDefault(x => x.PayPalPlanId == item.PayPalPlanId)?.Name ?? "[Deleted Plan]") 39 | View Details 40 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /BeerPal/Models/IdentityModels.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Security.Claims; 5 | using System.Threading.Tasks; 6 | using BeerPal.Entities; 7 | using Microsoft.AspNet.Identity; 8 | using Microsoft.AspNet.Identity.EntityFramework; 9 | 10 | namespace BeerPal.Models 11 | { 12 | // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more. 13 | public class ApplicationUser : IdentityUser 14 | { 15 | public ApplicationUser() 16 | { 17 | } 18 | 19 | public async Task GenerateUserIdentityAsync(UserManager manager) 20 | { 21 | // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType 22 | var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); 23 | // Add custom user claims here 24 | return userIdentity; 25 | } 26 | } 27 | 28 | public class ApplicationDbContext : IdentityDbContext 29 | { 30 | public DbSet Tickets { get; set; } 31 | public DbSet Orders { get; set; } 32 | public DbSet OrderItems { get; set; } 33 | public DbSet Beers { get; set; } 34 | public DbSet Subscriptions { get; set; } 35 | public DbSet WebhookEvents { get; set; } 36 | 37 | public ApplicationDbContext() 38 | : base("DefaultConnection", throwIfV1Schema: false) 39 | { 40 | } 41 | 42 | public static ApplicationDbContext Create() 43 | { 44 | return new ApplicationDbContext(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /BeerPal/Views/BillingPlans/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using Newtonsoft.Json 2 | @model PayPal.Api.PlanList 3 | 4 | @{ 5 | ViewBag.Title = "Billing Plans"; 6 | } 7 | 8 |
9 |
10 |
11 |
12 |

// Billing Plans

13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | @foreach (var plan in Model.plans) 36 | { 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | } 46 | 47 |
IDNameTypeStatePayment DefinitionsMerchant Preferences
@plan.id@plan.name@plan.type@plan.state@JsonConvert.SerializeObject(plan.payment_definitions)@JsonConvert.SerializeObject(plan.merchant_preferences)
48 |
49 |
50 |
51 |
52 | -------------------------------------------------------------------------------- /BeerPal/Views/ECommerce/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.ECommerce.IndexVm 2 | 3 | @{ 4 | ViewBag.Title = "Beers"; 5 | } 6 | 7 |
8 |
9 |
10 |
11 |

// Beers for Sale

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | @foreach (var item in Model.Beers) 31 | { 32 | 33 | 34 | 35 | 42 | 43 | } 44 | 45 |
BeerPrice
@item.Name$@((item.Price / 100M).ToString("0.00")) 36 | @using (Html.BeginForm("Add", "ECommerce", FormMethod.Post)) 37 | { 38 | @Html.Hidden("BeerId", item.Id) 39 | 40 | } 41 |
46 |
47 |
48 |
49 |
50 | -------------------------------------------------------------------------------- /BeerPal/Views/Subscription/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.Subscription.IndexVm 2 | 3 | @{ 4 | ViewBag.Title = "Subscription Plans"; 5 | } 6 | 7 |
8 |
9 |
10 |
11 |

// Beer Subscriptions

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | @foreach (var item in Model.Plans) 32 | { 33 | 34 | 35 | 36 | 37 | 40 | 41 | } 42 | 43 |
PlanDescriptionPrice
@item.Name@(item.NumberOfBeers) beer(s) delivered to your door. @(item.Description1). @item.Description2$@((item.Price / 100M).ToString("0.00")) 38 | Subscribe 39 |
44 |
45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /BeerPal/Public/css/wizard.css: -------------------------------------------------------------------------------- 1 | .nav-pills.nav-wizard > li { 2 | position: relative; 3 | overflow: visible; 4 | border-right: 15px solid transparent; 5 | border-left: 15px solid transparent; 6 | } 7 | .nav-pills.nav-wizard > li + li { 8 | margin-left: 0; 9 | } 10 | .nav-pills.nav-wizard > li:first-child { 11 | border-left: 0; 12 | } 13 | .nav-pills.nav-wizard > li:first-child a { 14 | border-radius: 5px 0 0 5px; 15 | } 16 | .nav-pills.nav-wizard > li:last-child { 17 | border-right: 0; 18 | } 19 | .nav-pills.nav-wizard > li:last-child a { 20 | border-radius: 0 5px 5px 0; 21 | } 22 | .nav-pills.nav-wizard > li a { 23 | border-radius: 0; 24 | background-color: #eee; 25 | } 26 | .nav-pills.nav-wizard > li:not(:last-child) a:after { 27 | position: absolute; 28 | content: ""; 29 | top: 0px; 30 | right: -20px; 31 | width: 0px; 32 | height: 0px; 33 | border-style: solid; 34 | border-width: 20px 0 20px 20px; 35 | border-color: transparent transparent transparent #eee; 36 | z-index: 150; 37 | } 38 | .nav-pills.nav-wizard > li:not(:first-child) a:before { 39 | position: absolute; 40 | content: ""; 41 | top: 0px; 42 | left: -20px; 43 | width: 0px; 44 | height: 0px; 45 | border-style: solid; 46 | border-width: 20px 0 20px 20px; 47 | border-color: #eee #eee #eee transparent; 48 | z-index: 150; 49 | } 50 | .nav-pills.nav-wizard > li:hover:not(:last-child) a:after { 51 | border-color: transparent transparent transparent #aaa; 52 | } 53 | .nav-pills.nav-wizard > li:hover:not(:first-child) a:before { 54 | border-color: #aaa #aaa #aaa transparent; 55 | } 56 | .nav-pills.nav-wizard > li:hover a { 57 | background-color: #aaa; 58 | color: #fff; 59 | } 60 | .nav-pills.nav-wizard > li.active:not(:last-child) a:after { 61 | border-color: transparent transparent transparent #428bca; 62 | } 63 | .nav-pills.nav-wizard > li.active:not(:first-child) a:before { 64 | border-color: #428bca #428bca #428bca transparent; 65 | } 66 | .nav-pills.nav-wizard > li.active a { 67 | background-color: #428bca; 68 | } -------------------------------------------------------------------------------- /BeerPal/Views/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /BeerPal/Views/Webhooks/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using Newtonsoft.Json 2 | @model PayPal.Api.WebhookList 3 | 4 | @{ 5 | ViewBag.Title = "Web Experience Profiles"; 6 | } 7 | 8 |
9 |
10 |
11 |
12 |

// Webhooks

13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @foreach (var webhook in Model.webhooks) 34 | { 35 | 36 | 37 | 38 | 39 | 46 | 47 | } 48 | 49 |
IDURLEvent Types
@webhook.id@webhook.url@JsonConvert.SerializeObject(webhook.event_types) 40 | @using (Html.BeginForm("Delete", "Webhooks", FormMethod.Post)) 41 | { 42 | @Html.Hidden("webhookId", webhook.id) 43 | 44 | } 45 |
50 |
51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /BeerPal/Migrations/201608251008534_AddedOrders.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class AddedOrders : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.OrderItems", 12 | c => new 13 | { 14 | Id = c.Int(nullable: false, identity: true), 15 | OrderId = c.Int(nullable: false), 16 | Name = c.String(), 17 | Price = c.Int(nullable: false), 18 | Quantity = c.Int(nullable: false), 19 | }) 20 | .PrimaryKey(t => t.Id) 21 | .ForeignKey("dbo.Orders", t => t.OrderId, cascadeDelete: true) 22 | .Index(t => t.OrderId); 23 | 24 | CreateTable( 25 | "dbo.Orders", 26 | c => new 27 | { 28 | Id = c.Int(nullable: false, identity: true), 29 | ApplicationUserId = c.Int(nullable: false), 30 | OrderDate = c.DateTime(nullable: false), 31 | Total = c.Int(nullable: false), 32 | PayPalReference = c.String(), 33 | ApplicationUser_Id = c.String(maxLength: 128), 34 | }) 35 | .PrimaryKey(t => t.Id) 36 | .ForeignKey("dbo.AspNetUsers", t => t.ApplicationUser_Id) 37 | .Index(t => t.ApplicationUser_Id); 38 | 39 | } 40 | 41 | public override void Down() 42 | { 43 | DropForeignKey("dbo.OrderItems", "OrderId", "dbo.Orders"); 44 | DropForeignKey("dbo.Orders", "ApplicationUser_Id", "dbo.AspNetUsers"); 45 | DropIndex("dbo.Orders", new[] { "ApplicationUser_Id" }); 46 | DropIndex("dbo.OrderItems", new[] { "OrderId" }); 47 | DropTable("dbo.Orders"); 48 | DropTable("dbo.OrderItems"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BeerPal/Models/Subscription/Plan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace BeerPal.Models.Subscription 7 | { 8 | public class Plan 9 | { 10 | public string PayPalPlanId { get; set; } 11 | public string Name { get; set; } 12 | public int Price { get; set; } 13 | public int NumberOfBeers { get; set; } 14 | public string Description1 { get; set; } 15 | public string Description2 { get; set; } 16 | 17 | public static List Plans => new List() 18 | { 19 | new Plan() 20 | { 21 | Name = "Just Browsing Plan", 22 | Price = 500, 23 | PayPalPlanId = "P-27H34871BG666983A2CPYWUI", // Created in the BillingPlansController. 24 | NumberOfBeers = 1, 25 | Description1 = "Nothing else", 26 | Description2 = "Not even a thank you" 27 | }, 28 | new Plan() 29 | { 30 | Name = "Let's Do This Plan", 31 | Price = 2495, 32 | PayPalPlanId = "P-6JH27250LV780153N2CPY5MA", // Created in the BillingPlansController. 33 | NumberOfBeers = 6, 34 | Description1 = "Welcome to the club", 35 | Description2 = "Thank you!" 36 | }, 37 | new Plan() 38 | { 39 | Name = "Beard Included Plan", 40 | Price = 5995, 41 | PayPalPlanId = "P-94257379NN474020F2CPZDWY", // Created in the BillingPlansController. 42 | NumberOfBeers = 24, 43 | Description1 = "This plan used to be a secret", 44 | Description2 = "Not anymore I guess" 45 | }, 46 | new Plan() 47 | { 48 | Name = "Hook It To My Veins Plan", 49 | Price = 10000, 50 | PayPalPlanId = "P-4H181978HP557725N2CPZKOI", // Created in the BillingPlansController. 51 | NumberOfBeers = 48, 52 | Description1 = "My personal plan", 53 | Description2 = "Should I just move in?" 54 | } 55 | }; 56 | } 57 | 58 | 59 | } -------------------------------------------------------------------------------- /BeerPal/Public/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d HttpContext.GetOwinContext().Get(); 15 | 16 | public ActionResult Index() 17 | { 18 | var subscriptions = _dbContext.Subscriptions.OrderByDescending(x => x.StartDate).Take(50).ToList(); 19 | 20 | return View(subscriptions); 21 | } 22 | 23 | public ActionResult Details(string id) 24 | { 25 | var apiContext = GetApiContext(); 26 | 27 | var agreement = Agreement.Get(apiContext, id); 28 | 29 | return View(agreement); 30 | } 31 | 32 | public ActionResult Suspend(string id) 33 | { 34 | var apiContext = GetApiContext(); 35 | 36 | Agreement.Suspend(apiContext, id, new AgreementStateDescriptor() 37 | { 38 | note = "Suspended" 39 | }); 40 | 41 | return RedirectToAction("Details", new {id = id}); 42 | } 43 | 44 | public ActionResult Reactivate(string id) 45 | { 46 | var apiContext = GetApiContext(); 47 | 48 | Agreement.ReActivate(apiContext, id, new AgreementStateDescriptor() 49 | { 50 | note = "Reactivated" 51 | }); 52 | 53 | return RedirectToAction("Details", new {id = id}); 54 | } 55 | 56 | public ActionResult Cancel(string id) 57 | { 58 | var apiContext = GetApiContext(); 59 | 60 | Agreement.Cancel(apiContext, id, new AgreementStateDescriptor() 61 | { 62 | note = "Cancelled" 63 | }); 64 | 65 | return RedirectToAction("Details", new {id = id}); 66 | } 67 | 68 | 69 | 70 | private APIContext GetApiContext() 71 | { 72 | // Authenticate with PayPal 73 | var config = ConfigManager.Instance.GetProperties(); 74 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 75 | var apiContext = new APIContext(accessToken); 76 | return apiContext; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /BeerPal/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /BeerPal/Public/css/wizard.less: -------------------------------------------------------------------------------- 1 | .nav-pills { 2 | &.nav-wizard { 3 | > li { 4 | position: relative; 5 | overflow: visible; 6 | border-right: 15px solid transparent; 7 | border-left: 15px solid transparent; 8 | 9 | + li { 10 | margin-left: 0; 11 | } 12 | 13 | &:first-child { 14 | border-left: 0; 15 | 16 | a { 17 | border-radius:5px 0 0 5px; 18 | } 19 | } 20 | 21 | &:last-child { 22 | border-right: 0; 23 | 24 | a { 25 | border-radius:0 5px 5px 0; 26 | } 27 | } 28 | 29 | a { 30 | border-radius: 0; 31 | background-color: #eee; 32 | } 33 | 34 | &:not(:last-child) a:after { 35 | position: absolute; 36 | content: ""; 37 | top: 0px; 38 | right: -20px; 39 | width: 0px; 40 | height: 0px; 41 | border-style: solid; 42 | border-width: 20px 0 20px 20px; 43 | border-color: transparent transparent transparent #eee; 44 | z-index: 150; 45 | } 46 | 47 | &:not(:first-child) a:before { 48 | position: absolute; 49 | content: ""; 50 | top: 0px; 51 | left: -20px; 52 | width: 0px; 53 | height: 0px; 54 | border-style: solid; 55 | border-width: 20px 0 20px 20px; 56 | border-color: #eee #eee #eee transparent; 57 | z-index: 150; 58 | } 59 | 60 | &:hover { 61 | &:not(:last-child) a:after { 62 | border-color: transparent transparent transparent #aaa; 63 | } 64 | 65 | &:not(:first-child) a:before { 66 | border-color: #aaa #aaa #aaa transparent; 67 | } 68 | 69 | a { 70 | background-color: #aaa; 71 | color: #fff; 72 | } 73 | } 74 | 75 | &.active { 76 | &:not(:last-child) a:after { 77 | border-color: transparent transparent transparent #428bca; 78 | } 79 | 80 | &:not(:first-child) a:before { 81 | border-color: #428bca #428bca #428bca transparent; 82 | } 83 | 84 | a { 85 | background-color: #428bca; 86 | } 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /BeerPal/Views/Single/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.Single.IndexVm 2 | 3 | @{ 4 | ViewBag.Title = "Brewery Tour"; 5 | } 6 | 7 |
8 |
9 |
10 |
11 |

// Brewery Tour

12 |
13 |
14 |
15 |
16 | 17 | 18 |
19 |
20 |
21 |
22 | @using (Html.BeginForm("Index", "Single", FormMethod.Post, new {@class = "form-horizontal"})) 23 | { 24 |

Ticket for @Model.TourDate.ToString("dddd, dd MMMM yyyy")

25 |
26 |
27 | 28 |
29 | @Html.TextBoxFor(x => x.FirstName, new {@class = "form-control"}) 30 |
31 | @Html.ValidationMessageFor(x => x.FirstName) 32 |
33 |
34 |
35 | 36 |
37 | 38 |
39 | @Html.TextBoxFor(x => x.LastName, new {@class = "form-control"}) 40 |
41 | @Html.ValidationMessageFor(x => x.LastName) 42 |
43 |
44 |
45 | 46 |
47 | 48 |
49 | @Html.TextBoxFor(x => x.Email, new {@class = "form-control"}) 50 |
51 | @Html.ValidationMessageFor(x => x.Email) 52 |
53 |
54 |
55 | 56 |
57 |
58 | 59 |
60 |
61 | } 62 |
63 |
64 |
65 |
66 | 67 | -------------------------------------------------------------------------------- /BeerPal/Views/Account/Login.cshtml: -------------------------------------------------------------------------------- 1 | @using BeerPal.Models 2 | @model LoginViewModel 3 | @{ 4 | ViewBag.Title = "Log in"; 5 | } 6 | 7 |

@ViewBag.Title.

8 |
9 |
10 |
11 | @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 12 | { 13 | @Html.AntiForgeryToken() 14 |

Use a local account to log in.

15 |
16 | @Html.ValidationSummary(true, "", new { @class = "text-danger" }) 17 |
18 | @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) 19 |
20 | @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) 21 | @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" }) 22 |
23 |
24 |
25 | @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) 26 |
27 | @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) 28 | @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" }) 29 |
30 |
31 |
32 |
33 |
34 | @Html.CheckBoxFor(m => m.RememberMe) 35 | @Html.LabelFor(m => m.RememberMe) 36 |
37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 |

45 | @Html.ActionLink("Register as a new user", "Register") 46 |

47 | @* Enable this once you have account confirmation enabled for password reset functionality 48 |

49 | @Html.ActionLink("Forgot your password?", "ForgotPassword") 50 |

*@ 51 | } 52 |
53 |
54 |
55 |
56 | @Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl }) 57 |
58 |
59 |
60 | 61 | @section Scripts { 62 | @Scripts.Render("~/bundles/jqueryval") 63 | } -------------------------------------------------------------------------------- /BeerPal/Views/Sales/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model PayPal.Api.PaymentHistory 2 | 3 | @{ 4 | ViewBag.Title = "Sales"; 5 | } 6 | 7 |
8 |
9 |
10 |
11 |

// Sales

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @foreach (var payment in Model.payments) 34 | { 35 | 36 | 37 | 38 | 39 | 40 | 41 | 55 | 56 | } 57 | 58 |
IDCreated DatePayment StatusTotalSale Status
@payment.id@payment.create_time@payment.state$@(decimal.Parse(payment.transactions.First().amount.total).ToString("0.00"))@(payment.transactions.First().related_resources.First(x => x.sale != null).sale.state) 42 | @if (payment.transactions.First().related_resources.First(x => x.sale != null).sale.state == "refunded") 43 | { 44 | 45 | } 46 | else 47 | { 48 | using (Html.BeginForm("Refund", "Sales", FormMethod.Post)) 49 | { 50 | @Html.Hidden("saleId", payment.transactions.First().related_resources.First(x => x.sale != null).sale.id) 51 | 52 | } 53 | } 54 |
59 |
60 |
61 |
62 |
63 | -------------------------------------------------------------------------------- /BeerPal/Controllers/WebhookEventsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | using BeerPal.Extras; 9 | using BeerPal.Models; 10 | using Microsoft.AspNet.Identity.Owin; 11 | using Newtonsoft.Json; 12 | using PayPal.Api; 13 | 14 | namespace BeerPal.Controllers 15 | { 16 | public class WebhookEventsController : Controller 17 | { 18 | private ApplicationDbContext _dbContext => HttpContext.GetOwinContext().Get(); 19 | 20 | public ActionResult Index() 21 | { 22 | var webhookEvents = _dbContext.WebhookEvents.OrderByDescending(x => x.DateReceived).Take(50).ToList(); 23 | 24 | return View(webhookEvents); 25 | } 26 | 27 | [HttpPost] 28 | public ActionResult SimulateEvent(string eventType) 29 | { 30 | var simlulatableWebhook = new SimulatableWebhook(); 31 | var webhookEvent = simlulatableWebhook.SimulateEvent(eventType, "0L586542FF7643339"); 32 | 33 | return RedirectToAction("Index"); 34 | } 35 | 36 | [HttpPost] 37 | public ActionResult Receive() 38 | { 39 | var apiContext = GetApiContext(); 40 | 41 | var postBody = new StreamReader(Request.InputStream).ReadToEnd(); 42 | var webhookEvent = JsonConvert.DeserializeObject(postBody); 43 | 44 | // Ensure this event was genuinely sent by PayPal 45 | var isValid = WebhookEvent.ValidateReceivedEvent(apiContext, Request.Headers, postBody, "0L586542FF7643339"); // Last parameter is your Webhook ID 46 | 47 | if (!isValid) 48 | { 49 | return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 50 | } 51 | 52 | var localEvent = new Entities.WebhookEvent() 53 | { 54 | EventType = webhookEvent.event_type, 55 | PayPalWebHookEventId = webhookEvent.id, 56 | ResourceType = webhookEvent.resource_type, 57 | Summary = webhookEvent.summary, 58 | ResourceJson = JsonConvert.SerializeObject(webhookEvent.resource), 59 | DateCreated = DateTime.Parse(webhookEvent.create_time), 60 | DateReceived = DateTime.UtcNow 61 | }; 62 | _dbContext.WebhookEvents.Add(localEvent); 63 | _dbContext.SaveChanges(); 64 | 65 | return new HttpStatusCodeResult(HttpStatusCode.OK); 66 | } 67 | 68 | private APIContext GetApiContext() 69 | { 70 | // Authenticate with PayPal 71 | var config = ConfigManager.Instance.GetProperties(); 72 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 73 | var apiContext = new APIContext(accessToken); 74 | return apiContext; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /BeerPal/Models/ManageViewModels.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using Microsoft.AspNet.Identity; 4 | using Microsoft.Owin.Security; 5 | 6 | namespace BeerPal.Models 7 | { 8 | public class IndexViewModel 9 | { 10 | public bool HasPassword { get; set; } 11 | public IList Logins { get; set; } 12 | public string PhoneNumber { get; set; } 13 | public bool TwoFactor { get; set; } 14 | public bool BrowserRemembered { get; set; } 15 | } 16 | 17 | public class ManageLoginsViewModel 18 | { 19 | public IList CurrentLogins { get; set; } 20 | public IList OtherLogins { get; set; } 21 | } 22 | 23 | public class FactorViewModel 24 | { 25 | public string Purpose { get; set; } 26 | } 27 | 28 | public class SetPasswordViewModel 29 | { 30 | [Required] 31 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 32 | [DataType(DataType.Password)] 33 | [Display(Name = "New password")] 34 | public string NewPassword { get; set; } 35 | 36 | [DataType(DataType.Password)] 37 | [Display(Name = "Confirm new password")] 38 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 39 | public string ConfirmPassword { get; set; } 40 | } 41 | 42 | public class ChangePasswordViewModel 43 | { 44 | [Required] 45 | [DataType(DataType.Password)] 46 | [Display(Name = "Current password")] 47 | public string OldPassword { get; set; } 48 | 49 | [Required] 50 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 51 | [DataType(DataType.Password)] 52 | [Display(Name = "New password")] 53 | public string NewPassword { get; set; } 54 | 55 | [DataType(DataType.Password)] 56 | [Display(Name = "Confirm new password")] 57 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 58 | public string ConfirmPassword { get; set; } 59 | } 60 | 61 | public class AddPhoneNumberViewModel 62 | { 63 | [Required] 64 | [Phone] 65 | [Display(Name = "Phone Number")] 66 | public string Number { get; set; } 67 | } 68 | 69 | public class VerifyPhoneNumberViewModel 70 | { 71 | [Required] 72 | [Display(Name = "Code")] 73 | public string Code { get; set; } 74 | 75 | [Required] 76 | [Phone] 77 | [Display(Name = "Phone Number")] 78 | public string PhoneNumber { get; set; } 79 | } 80 | 81 | public class ConfigureTwoFactorViewModel 82 | { 83 | public string SelectedProvider { get; set; } 84 | public ICollection Providers { get; set; } 85 | } 86 | } -------------------------------------------------------------------------------- /BeerPal/Views/Manage/ManageLogins.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.ManageLoginsViewModel 2 | @using Microsoft.Owin.Security 3 | @{ 4 | ViewBag.Title = "Manage your external logins"; 5 | } 6 | 7 |

@ViewBag.Title.

8 | 9 |

@ViewBag.StatusMessage

10 | @{ 11 | var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes(); 12 | if (loginProviders.Count() == 0) { 13 |
14 |

15 | There are no external authentication services configured. See this article 16 | for details on setting up this ASP.NET application to support logging in via external services. 17 |

18 |
19 | } 20 | else 21 | { 22 | if (Model.CurrentLogins.Count > 0) 23 | { 24 |

Registered Logins

25 | 26 | 27 | @foreach (var account in Model.CurrentLogins) 28 | { 29 | 30 | 31 | 49 | 50 | } 51 | 52 |
@account.LoginProvider 32 | @if (ViewBag.ShowRemoveButton) 33 | { 34 | using (Html.BeginForm("RemoveLogin", "Manage")) 35 | { 36 | @Html.AntiForgeryToken() 37 |
38 | @Html.Hidden("loginProvider", account.LoginProvider) 39 | @Html.Hidden("providerKey", account.ProviderKey) 40 | 41 |
42 | } 43 | } 44 | else 45 | { 46 | @:   47 | } 48 |
53 | } 54 | if (Model.OtherLogins.Count > 0) 55 | { 56 | using (Html.BeginForm("LinkLogin", "Manage")) 57 | { 58 | @Html.AntiForgeryToken() 59 |
60 |

61 | @foreach (AuthenticationDescription p in Model.OtherLogins) 62 | { 63 | 64 | } 65 |

66 |
67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /BeerPal/Controllers/WebhooksController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | using Microsoft.Owin.Security.Twitter.Messages; 9 | using Newtonsoft.Json; 10 | using PayPal.Api; 11 | 12 | namespace BeerPal.Controllers 13 | { 14 | public class WebhooksController : Controller 15 | { 16 | // GET: Webhooks 17 | public ActionResult Index() 18 | { 19 | var apiContext = GetApiContext(); 20 | 21 | var list = Webhook.GetAll(apiContext); 22 | 23 | if (!list.webhooks.Any()) 24 | { 25 | SeedWebhooks(apiContext); 26 | list = Webhook.GetAll(apiContext); 27 | } 28 | 29 | return View(list); 30 | } 31 | 32 | [HttpPost] 33 | public ActionResult Delete(string webhookId) 34 | { 35 | var apiContext = GetApiContext(); 36 | 37 | var webhook = new Webhook() 38 | { 39 | id = webhookId 40 | }; 41 | 42 | webhook.Delete(apiContext); 43 | 44 | return RedirectToAction("Index"); 45 | } 46 | 47 | private void SeedWebhooks(APIContext apiContext) 48 | { 49 | var callbackUrl = Url.Action("Receive", "WebhookEvents", null, Request.Url.Scheme); 50 | 51 | if (Request.Url.Host == "localhost") 52 | { 53 | // Replace with your Ngrok tunnel url 54 | callbackUrl = "https://f156f219.ngrok.io/WebhookEvents/Receive"; 55 | } 56 | 57 | var everythingWebhook = new Webhook() 58 | { 59 | url = callbackUrl, 60 | event_types = new List 61 | { 62 | new WebhookEventType 63 | { 64 | name = "PAYMENT.SALE.REFUNDED" 65 | }, 66 | new WebhookEventType 67 | { 68 | name = "PAYMENT.SALE.REVERSED" 69 | }, 70 | new WebhookEventType 71 | { 72 | name = "CUSTOMER.DISPUTE.CREATED" 73 | }, 74 | new WebhookEventType 75 | { 76 | name = "BILLING.SUBSCRIPTION.CANCELLED" 77 | }, 78 | new WebhookEventType 79 | { 80 | name = "BILLING.SUBSCRIPTION.SUSPENDED" 81 | }, 82 | new WebhookEventType 83 | { 84 | name = "BILLING.SUBSCRIPTION.RE-ACTIVATED" 85 | }, 86 | } 87 | }; 88 | Webhook.Create(apiContext, everythingWebhook); 89 | } 90 | 91 | private APIContext GetApiContext() 92 | { 93 | // Authenticate with PayPal 94 | var config = ConfigManager.Instance.GetProperties(); 95 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 96 | var apiContext = new APIContext(accessToken); 97 | return apiContext; 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /BeerPal/Views/WebhookEvents/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | 3 | @{ 4 | ViewBag.Title = "Sales"; 5 | } 6 | 7 |
8 |
9 |
10 |
11 |

// Webhook Events

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 |

Simulate Events

22 |
23 | @using (Html.BeginForm("SimulateEvent", "WebhookEvents", FormMethod.Post)) 24 | { 25 | @Html.Hidden("eventType", "PAYMENT.SALE.REFUNDED") 26 | 27 | } 28 |
29 |
30 | @using (Html.BeginForm("SimulateEvent", "WebhookEvents", FormMethod.Post)) 31 | { 32 | @Html.Hidden("eventType", "BILLING.SUBSCRIPTION.CANCELLED") 33 | 34 | } 35 |
36 |
37 | @using (Html.BeginForm("SimulateEvent", "WebhookEvents", FormMethod.Post)) 38 | { 39 | @Html.Hidden("eventType", "CUSTOMER.DISPUTE.CREATED") 40 | 41 | } 42 |
43 |

Notifications may take a few minutes to appear.

44 |
45 |
46 |
47 |
48 |

Received Events

49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | @foreach (var webhookEvent in Model) 61 | { 62 | 63 | 64 | 65 | 66 | 67 | 74 | 75 | } 76 | 77 |
IDCreated DateEvent TypeSummary
@webhookEvent.PayPalWebHookEventId@webhookEvent.DateCreated (Received: @webhookEvent.DateReceived)@webhookEvent.EventType@webhookEvent.Summary 68 | @using (Html.BeginForm("Resend", "WebhookEvents", FormMethod.Post)) 69 | { 70 | @Html.Hidden("webhookEventId", webhookEvent.PayPalWebHookEventId) 71 | 72 | } 73 |
78 |
79 |
80 |
81 |
82 | -------------------------------------------------------------------------------- /BeerPal/Views/Subscription/Purchase.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.Subscription.PurchaseVm 2 | 3 | @{ 4 | ViewBag.Title = "Beer Subscription"; 5 | } 6 | 7 | 8 |
9 |
10 |
11 |
12 |

// Beer Subscription

13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | @using (Html.BeginForm("Purchase", "Subscription", FormMethod.Post, new {@class = "form-horizontal"})) 24 | { 25 | @Html.HiddenFor(x => x.Plan.PayPalPlanId) 26 | 27 |

@Model.Plan.Name

28 |
29 |
30 | 31 |
32 | @Html.TextBoxFor(x => x.FirstName, new {@class = "form-control"}) 33 |
34 | @Html.ValidationMessageFor(x => x.FirstName) 35 |
36 |
37 |
38 | 39 |
40 | 41 |
42 | @Html.TextBoxFor(x => x.LastName, new {@class = "form-control"}) 43 |
44 | @Html.ValidationMessageFor(x => x.LastName) 45 |
46 |
47 |
48 | 49 |
50 | 51 |
52 | @Html.TextBoxFor(x => x.Email, new {@class = "form-control"}) 53 |
54 | @Html.ValidationMessageFor(x => x.Email) 55 |
56 |
57 |
58 | 59 |
60 |
61 | 62 |
63 |
64 | } 65 |
66 |
67 |
    68 |
  • @Model.Plan.Name
  • 69 |
  • 70 | $ @((Model.Plan.Price / 100M).ToString("0.00")) 71 |

    Monthly

    72 |

    73 |
  • 74 |
  • @Model.Plan.NumberOfBeers Beer@(Model.Plan.NumberOfBeers != 1 ? "s" : "")
  • 75 |
  • @Model.Plan.Description1
  • 76 |
  • @Model.Plan.Description2
  • 77 |
78 |
79 |
80 |
81 |
82 | 83 | 84 | -------------------------------------------------------------------------------- /BeerPal/Views/Manage/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.IndexViewModel 2 | @{ 3 | ViewBag.Title = "Manage"; 4 | } 5 | 6 |

@ViewBag.Title.

7 | 8 |

@ViewBag.StatusMessage

9 |
10 |

Change your account settings

11 |
12 |
13 |
Password:
14 |
15 | [ 16 | @if (Model.HasPassword) 17 | { 18 | @Html.ActionLink("Change your password", "ChangePassword") 19 | } 20 | else 21 | { 22 | @Html.ActionLink("Create", "SetPassword") 23 | } 24 | ] 25 |
26 |
External Logins:
27 |
28 | @Model.Logins.Count [ 29 | @Html.ActionLink("Manage", "ManageLogins") ] 30 |
31 | @* 32 | Phone Numbers can used as a second factor of verification in a two-factor authentication system. 33 | 34 | See this article 35 | for details on setting up this ASP.NET application to support two-factor authentication using SMS. 36 | 37 | Uncomment the following block after you have set up two-factor authentication 38 | *@ 39 | @* 40 |
Phone Number:
41 |
42 | @(Model.PhoneNumber ?? "None") 43 | @if (Model.PhoneNumber != null) 44 | { 45 |
46 | [  @Html.ActionLink("Change", "AddPhoneNumber")  ] 47 | using (Html.BeginForm("RemovePhoneNumber", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 48 | { 49 | @Html.AntiForgeryToken() 50 | [] 51 | } 52 | } 53 | else 54 | { 55 | [  @Html.ActionLink("Add", "AddPhoneNumber") 56 | } 57 |
58 | *@ 59 |
Two-Factor Authentication:
60 |
61 |

62 | There are no two-factor authentication providers configured. See this article 63 | for details on setting up this ASP.NET application to support two-factor authentication. 64 |

65 | @*@if (Model.TwoFactor) 66 | { 67 | using (Html.BeginForm("DisableTwoFactorAuthentication", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 68 | { 69 | @Html.AntiForgeryToken() 70 | Enabled 71 | 72 | 73 | } 74 | } 75 | else 76 | { 77 | using (Html.BeginForm("EnableTwoFactorAuthentication", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 78 | { 79 | @Html.AntiForgeryToken() 80 | Disabled 81 | 82 | 83 | } 84 | }*@ 85 |
86 |
87 |
88 | -------------------------------------------------------------------------------- /BeerPal/Models/AccountViewModels.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace BeerPal.Models 5 | { 6 | public class ExternalLoginConfirmationViewModel 7 | { 8 | [Required] 9 | [Display(Name = "Email")] 10 | public string Email { get; set; } 11 | } 12 | 13 | public class ExternalLoginListViewModel 14 | { 15 | public string ReturnUrl { get; set; } 16 | } 17 | 18 | public class SendCodeViewModel 19 | { 20 | public string SelectedProvider { get; set; } 21 | public ICollection Providers { get; set; } 22 | public string ReturnUrl { get; set; } 23 | public bool RememberMe { get; set; } 24 | } 25 | 26 | public class VerifyCodeViewModel 27 | { 28 | [Required] 29 | public string Provider { get; set; } 30 | 31 | [Required] 32 | [Display(Name = "Code")] 33 | public string Code { get; set; } 34 | public string ReturnUrl { get; set; } 35 | 36 | [Display(Name = "Remember this browser?")] 37 | public bool RememberBrowser { get; set; } 38 | 39 | public bool RememberMe { get; set; } 40 | } 41 | 42 | public class ForgotViewModel 43 | { 44 | [Required] 45 | [Display(Name = "Email")] 46 | public string Email { get; set; } 47 | } 48 | 49 | public class LoginViewModel 50 | { 51 | [Required] 52 | [Display(Name = "Email")] 53 | [EmailAddress] 54 | public string Email { get; set; } 55 | 56 | [Required] 57 | [DataType(DataType.Password)] 58 | [Display(Name = "Password")] 59 | public string Password { get; set; } 60 | 61 | [Display(Name = "Remember me?")] 62 | public bool RememberMe { get; set; } 63 | } 64 | 65 | public class RegisterViewModel 66 | { 67 | [Required] 68 | [EmailAddress] 69 | [Display(Name = "Email")] 70 | public string Email { get; set; } 71 | 72 | [Required] 73 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 74 | [DataType(DataType.Password)] 75 | [Display(Name = "Password")] 76 | public string Password { get; set; } 77 | 78 | [DataType(DataType.Password)] 79 | [Display(Name = "Confirm password")] 80 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 81 | public string ConfirmPassword { get; set; } 82 | } 83 | 84 | public class ResetPasswordViewModel 85 | { 86 | [Required] 87 | [EmailAddress] 88 | [Display(Name = "Email")] 89 | public string Email { get; set; } 90 | 91 | [Required] 92 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 93 | [DataType(DataType.Password)] 94 | [Display(Name = "Password")] 95 | public string Password { get; set; } 96 | 97 | [DataType(DataType.Password)] 98 | [Display(Name = "Confirm password")] 99 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 100 | public string ConfirmPassword { get; set; } 101 | 102 | public string Code { get; set; } 103 | } 104 | 105 | public class ForgotPasswordViewModel 106 | { 107 | [Required] 108 | [EmailAddress] 109 | [Display(Name = "Email")] 110 | public string Email { get; set; } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /BeerPal/App_Start/Startup.Auth.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity; 3 | using BeerPal.Migrations; 4 | using Microsoft.AspNet.Identity; 5 | using Microsoft.AspNet.Identity.Owin; 6 | using Microsoft.Owin; 7 | using Microsoft.Owin.Security.Cookies; 8 | using Microsoft.Owin.Security.Google; 9 | using Owin; 10 | using BeerPal.Models; 11 | 12 | namespace BeerPal 13 | { 14 | public partial class Startup 15 | { 16 | // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864 17 | public void ConfigureAuth(IAppBuilder app) 18 | { 19 | // Configure the db context, user manager and signin manager to use a single instance per request 20 | app.CreatePerOwinContext(ApplicationDbContext.Create); 21 | app.CreatePerOwinContext(ApplicationUserManager.Create); 22 | app.CreatePerOwinContext(ApplicationSignInManager.Create); 23 | Database.SetInitializer(new MigrateDatabaseToLatestVersion()); 24 | 25 | // Enable the application to use a cookie to store information for the signed in user 26 | // and to use a cookie to temporarily store information about a user logging in with a third party login provider 27 | // Configure the sign in cookie 28 | app.UseCookieAuthentication(new CookieAuthenticationOptions 29 | { 30 | AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 31 | LoginPath = new PathString("/Account/Login"), 32 | Provider = new CookieAuthenticationProvider 33 | { 34 | // Enables the application to validate the security stamp when the user logs in. 35 | // This is a security feature which is used when you change a password or add an external login to your account. 36 | OnValidateIdentity = SecurityStampValidator.OnValidateIdentity( 37 | validateInterval: TimeSpan.FromMinutes(30), 38 | regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) 39 | } 40 | }); 41 | app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); 42 | 43 | // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process. 44 | app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5)); 45 | 46 | // Enables the application to remember the second login verification factor such as phone or email. 47 | // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from. 48 | // This is similar to the RememberMe option when you log in. 49 | app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); 50 | 51 | // Uncomment the following lines to enable logging in with third party login providers 52 | //app.UseMicrosoftAccountAuthentication( 53 | // clientId: "", 54 | // clientSecret: ""); 55 | 56 | //app.UseTwitterAuthentication( 57 | // consumerKey: "", 58 | // consumerSecret: ""); 59 | 60 | //app.UseFacebookAuthentication( 61 | // appId: "", 62 | // appSecret: ""); 63 | 64 | //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions() 65 | //{ 66 | // ClientId = "", 67 | // ClientSecret = "" 68 | //}); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /BeerPal/Views/ECommerce/Cart.cshtml: -------------------------------------------------------------------------------- 1 | @using Newtonsoft.Json 2 | @model BeerPal.Models.ECommerce.Cart 3 | 4 | @{ 5 | ViewBag.Title = "Shopping Cart"; 6 | } 7 | 8 |
9 |
10 |
11 |
12 |

// Shopping Cart

13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @foreach (var item in Model.CartItems) 34 | { 35 | 36 | 37 | 38 | 39 | 40 | 47 | 48 | } 49 | @if (!Model.CartItems.Any()) 50 | { 51 | 52 | 53 | 54 | } 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
BeerPriceQuantitySub Total
@item.Name$@((item.Price / 100M).ToString("0.00"))@item.Quantity$@(((item.Price * item.Quantity) / 100M).ToString("0.00")) 41 | @using (Html.BeginForm("Delete", "ECommerce", FormMethod.Post)) 42 | { 43 | @Html.Hidden("BeerId", item.BeerId) 44 | 45 | } 46 |
No items in shopping cart
Total$@((Model.CartItems.Sum(x => x.Price * x.Quantity) / 100M).ToString("0.00"))
64 |
65 | 66 |

67 | Checkout with PayPal 68 |

69 |
70 |
71 | 72 |
73 | 82 | 83 |
84 |
85 |
86 |
87 |
88 | 89 | -------------------------------------------------------------------------------- /BeerPal/Public/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 2 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 3 | window.matchMedia=window.matchMedia||function(a){"use strict";var c,d=a.documentElement,e=d.firstElementChild||d.firstChild,f=a.createElement("body"),g=a.createElement("div");return g.id="mq-test-1",g.style.cssText="position:absolute;top:-100em",f.style.background="none",f.appendChild(g),function(a){return g.innerHTML='­',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document); 4 | 5 | /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 6 | (function(a){"use strict";function x(){u(!0)}var b={};a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,b.mediaQueriesSupported;var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var a=m.shift();v(a.href,function(b){p(b,a.href,a.media),h[a.href]=!0,setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(a){var b="clientWidth",h=d[b],k="CSS1Compat"===c.compatMode&&h||c.body[b]||h,m={},n=l[l.length-1],o=(new Date).getTime();if(a&&q&&i>o-q)return clearTimeout(r),r=setTimeout(u,i),void 0;q=o;for(var p in e)if(e.hasOwnProperty(p)){var v=e[p],w=v.minw,x=v.maxw,y=null===w,z=null===x,A="em";w&&(w=parseFloat(w)*(w.indexOf(A)>-1?t||s():1)),x&&(x=parseFloat(x)*(x.indexOf(A)>-1?t||s():1)),v.hasquery&&(y&&z||!(y||k>=w)||!(z||x>=k))||(m[v.media]||(m[v.media]=[]),m[v.media].push(f[v.rules]))}for(var B in g)g.hasOwnProperty(B)&&g[B]&&g[B].parentNode===j&&j.removeChild(g[B]);for(var C in m)if(m.hasOwnProperty(C)){var D=c.createElement("style"),E=m[C].join("\n");D.type="text/css",D.media=C,j.insertBefore(D,n.nextSibling),D.styleSheet?D.styleSheet.cssText=E:D.appendChild(c.createTextNode(E)),g.push(D)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)})(this); 7 | -------------------------------------------------------------------------------- /BeerPal/Scripts/respond.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='­';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); 18 | 19 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 20 | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); -------------------------------------------------------------------------------- /BeerPal/App_Start/IdentityConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Security.Claims; 6 | using System.Threading.Tasks; 7 | using System.Web; 8 | using Microsoft.AspNet.Identity; 9 | using Microsoft.AspNet.Identity.EntityFramework; 10 | using Microsoft.AspNet.Identity.Owin; 11 | using Microsoft.Owin; 12 | using Microsoft.Owin.Security; 13 | using BeerPal.Models; 14 | 15 | namespace BeerPal 16 | { 17 | public class EmailService : IIdentityMessageService 18 | { 19 | public Task SendAsync(IdentityMessage message) 20 | { 21 | // Plug in your email service here to send an email. 22 | return Task.FromResult(0); 23 | } 24 | } 25 | 26 | public class SmsService : IIdentityMessageService 27 | { 28 | public Task SendAsync(IdentityMessage message) 29 | { 30 | // Plug in your SMS service here to send a text message. 31 | return Task.FromResult(0); 32 | } 33 | } 34 | 35 | // Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application. 36 | public class ApplicationUserManager : UserManager 37 | { 38 | public ApplicationUserManager(IUserStore store) 39 | : base(store) 40 | { 41 | } 42 | 43 | public static ApplicationUserManager Create(IdentityFactoryOptions options, IOwinContext context) 44 | { 45 | var manager = new ApplicationUserManager(new UserStore(context.Get())); 46 | // Configure validation logic for usernames 47 | manager.UserValidator = new UserValidator(manager) 48 | { 49 | AllowOnlyAlphanumericUserNames = false, 50 | RequireUniqueEmail = true 51 | }; 52 | 53 | // Configure validation logic for passwords 54 | manager.PasswordValidator = new PasswordValidator 55 | { 56 | RequiredLength = 6, 57 | RequireNonLetterOrDigit = true, 58 | RequireDigit = true, 59 | RequireLowercase = true, 60 | RequireUppercase = true, 61 | }; 62 | 63 | // Configure user lockout defaults 64 | manager.UserLockoutEnabledByDefault = true; 65 | manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5); 66 | manager.MaxFailedAccessAttemptsBeforeLockout = 5; 67 | 68 | // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user 69 | // You can write your own provider and plug it in here. 70 | manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider 71 | { 72 | MessageFormat = "Your security code is {0}" 73 | }); 74 | manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider 75 | { 76 | Subject = "Security Code", 77 | BodyFormat = "Your security code is {0}" 78 | }); 79 | manager.EmailService = new EmailService(); 80 | manager.SmsService = new SmsService(); 81 | var dataProtectionProvider = options.DataProtectionProvider; 82 | if (dataProtectionProvider != null) 83 | { 84 | manager.UserTokenProvider = 85 | new DataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")); 86 | } 87 | return manager; 88 | } 89 | } 90 | 91 | // Configure the application sign-in manager which is used in this application. 92 | public class ApplicationSignInManager : SignInManager 93 | { 94 | public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) 95 | : base(userManager, authenticationManager) 96 | { 97 | } 98 | 99 | public override Task CreateUserIdentityAsync(ApplicationUser user) 100 | { 101 | return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager); 102 | } 103 | 104 | public static ApplicationSignInManager Create(IdentityFactoryOptions options, IOwinContext context) 105 | { 106 | return new ApplicationSignInManager(context.GetUserManager(), context.Authentication); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /BeerPal/Controllers/SubscriptionController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using BeerPal.Entities; 7 | using BeerPal.Models; 8 | using BeerPal.Models.Subscription; 9 | using Microsoft.AspNet.Identity.Owin; 10 | using PayPal.Api; 11 | using Plan = BeerPal.Models.Subscription.Plan; 12 | 13 | namespace BeerPal.Controllers 14 | { 15 | public class SubscriptionController : Controller 16 | { 17 | private ApplicationDbContext _dbContext => HttpContext.GetOwinContext().Get(); 18 | 19 | public ActionResult Index() 20 | { 21 | var model = new IndexVm() 22 | { 23 | Plans = Plan.Plans 24 | }; 25 | 26 | return View(model); 27 | } 28 | 29 | public ActionResult Purchase(string id) 30 | { 31 | var model = new PurchaseVm() 32 | { 33 | Plan = Plan.Plans.FirstOrDefault(x => x.PayPalPlanId == id) 34 | }; 35 | 36 | return View(model); 37 | } 38 | 39 | [HttpPost] 40 | public ActionResult Purchase(PurchaseVm model) 41 | { 42 | var plan = Plan.Plans.FirstOrDefault(x => x.PayPalPlanId == model.Plan.PayPalPlanId); 43 | 44 | if (ModelState.IsValid) 45 | { 46 | // Since we take an Initial Payment (instant payment), the start date of the recurring payments will be next month. 47 | var startDate = DateTime.UtcNow.AddMonths(1); 48 | 49 | var apiContext = GetApiContext(); 50 | 51 | var subscription = new Subscription() 52 | { 53 | FirstName = model.FirstName, 54 | LastName = model.LastName, 55 | Email = model.Email, 56 | StartDate = startDate, 57 | PayPalPlanId = plan.PayPalPlanId 58 | }; 59 | _dbContext.Subscriptions.Add(subscription); 60 | _dbContext.SaveChanges(); 61 | 62 | var agreement = new Agreement() 63 | { 64 | name = plan.Name, 65 | description = $"{plan.NumberOfBeers} beer(s) delivered for ${(plan.Price/100M).ToString("0.00")} each month.", 66 | start_date = startDate.ToString("yyyy-MM-ddTHH:mm:ssZ"), 67 | plan = new PayPal.Api.Plan() 68 | { 69 | id = plan.PayPalPlanId 70 | }, 71 | payer = new Payer() 72 | { 73 | payment_method = "paypal" 74 | } 75 | }; 76 | 77 | // Send the agreement to PayPal 78 | var createdAgreement = agreement.Create(apiContext); 79 | 80 | // Save the token so we can match the returned request to our subscription. 81 | subscription.PayPalAgreementToken = createdAgreement.token; 82 | _dbContext.SaveChanges(); 83 | 84 | // Find the Approval URL to send our user to 85 | var approvalUrl = 86 | createdAgreement.links.FirstOrDefault( 87 | x => x.rel.Equals("approval_url", StringComparison.OrdinalIgnoreCase)); 88 | 89 | // Send the user to PayPal to approve the payment 90 | return Redirect(approvalUrl.href); 91 | } 92 | 93 | model.Plan = plan; 94 | return View(model); 95 | } 96 | 97 | public ActionResult Return(string token) 98 | { 99 | var subscription = _dbContext.Subscriptions.FirstOrDefault(x => x.PayPalAgreementToken == token); 100 | 101 | var apiContext = GetApiContext(); 102 | 103 | var agreement = new Agreement() 104 | { 105 | token = token 106 | }; 107 | 108 | var executedAgreement = agreement.Execute(apiContext); 109 | 110 | // Save the PayPal agreement in our subscription so we can look it up later. 111 | subscription.PayPalAgreementId = executedAgreement.id; 112 | _dbContext.SaveChanges(); 113 | 114 | return RedirectToAction("Thankyou"); 115 | } 116 | 117 | public ActionResult Cancel() 118 | { 119 | return View(); 120 | } 121 | 122 | public ActionResult ThankYou() 123 | { 124 | return View(); 125 | } 126 | 127 | private APIContext GetApiContext() 128 | { 129 | // Authenticate with PayPal 130 | var config = ConfigManager.Instance.GetProperties(); 131 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 132 | var apiContext = new APIContext(accessToken); 133 | return apiContext; 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /BeerPal/Migrations/201608250815248_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | namespace BeerPal.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class InitialMigration : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.AspNetRoles", 12 | c => new 13 | { 14 | Id = c.String(nullable: false, maxLength: 128), 15 | Name = c.String(nullable: false, maxLength: 256), 16 | }) 17 | .PrimaryKey(t => t.Id) 18 | .Index(t => t.Name, unique: true, name: "RoleNameIndex"); 19 | 20 | CreateTable( 21 | "dbo.AspNetUserRoles", 22 | c => new 23 | { 24 | UserId = c.String(nullable: false, maxLength: 128), 25 | RoleId = c.String(nullable: false, maxLength: 128), 26 | }) 27 | .PrimaryKey(t => new { t.UserId, t.RoleId }) 28 | .ForeignKey("dbo.AspNetRoles", t => t.RoleId, cascadeDelete: true) 29 | .ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true) 30 | .Index(t => t.UserId) 31 | .Index(t => t.RoleId); 32 | 33 | CreateTable( 34 | "dbo.Tickets", 35 | c => new 36 | { 37 | Id = c.Int(nullable: false, identity: true), 38 | FirstName = c.String(), 39 | LastName = c.String(), 40 | Email = c.String(), 41 | TourDate = c.DateTime(nullable: false), 42 | PayPalReference = c.String(), 43 | }) 44 | .PrimaryKey(t => t.Id); 45 | 46 | CreateTable( 47 | "dbo.AspNetUsers", 48 | c => new 49 | { 50 | Id = c.String(nullable: false, maxLength: 128), 51 | Email = c.String(maxLength: 256), 52 | EmailConfirmed = c.Boolean(nullable: false), 53 | PasswordHash = c.String(), 54 | SecurityStamp = c.String(), 55 | PhoneNumber = c.String(), 56 | PhoneNumberConfirmed = c.Boolean(nullable: false), 57 | TwoFactorEnabled = c.Boolean(nullable: false), 58 | LockoutEndDateUtc = c.DateTime(), 59 | LockoutEnabled = c.Boolean(nullable: false), 60 | AccessFailedCount = c.Int(nullable: false), 61 | UserName = c.String(nullable: false, maxLength: 256), 62 | }) 63 | .PrimaryKey(t => t.Id) 64 | .Index(t => t.UserName, unique: true, name: "UserNameIndex"); 65 | 66 | CreateTable( 67 | "dbo.AspNetUserClaims", 68 | c => new 69 | { 70 | Id = c.Int(nullable: false, identity: true), 71 | UserId = c.String(nullable: false, maxLength: 128), 72 | ClaimType = c.String(), 73 | ClaimValue = c.String(), 74 | }) 75 | .PrimaryKey(t => t.Id) 76 | .ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true) 77 | .Index(t => t.UserId); 78 | 79 | CreateTable( 80 | "dbo.AspNetUserLogins", 81 | c => new 82 | { 83 | LoginProvider = c.String(nullable: false, maxLength: 128), 84 | ProviderKey = c.String(nullable: false, maxLength: 128), 85 | UserId = c.String(nullable: false, maxLength: 128), 86 | }) 87 | .PrimaryKey(t => new { t.LoginProvider, t.ProviderKey, t.UserId }) 88 | .ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true) 89 | .Index(t => t.UserId); 90 | 91 | } 92 | 93 | public override void Down() 94 | { 95 | DropForeignKey("dbo.AspNetUserRoles", "UserId", "dbo.AspNetUsers"); 96 | DropForeignKey("dbo.AspNetUserLogins", "UserId", "dbo.AspNetUsers"); 97 | DropForeignKey("dbo.AspNetUserClaims", "UserId", "dbo.AspNetUsers"); 98 | DropForeignKey("dbo.AspNetUserRoles", "RoleId", "dbo.AspNetRoles"); 99 | DropIndex("dbo.AspNetUserLogins", new[] { "UserId" }); 100 | DropIndex("dbo.AspNetUserClaims", new[] { "UserId" }); 101 | DropIndex("dbo.AspNetUsers", "UserNameIndex"); 102 | DropIndex("dbo.AspNetUserRoles", new[] { "RoleId" }); 103 | DropIndex("dbo.AspNetUserRoles", new[] { "UserId" }); 104 | DropIndex("dbo.AspNetRoles", "RoleNameIndex"); 105 | DropTable("dbo.AspNetUserLogins"); 106 | DropTable("dbo.AspNetUserClaims"); 107 | DropTable("dbo.AspNetUsers"); 108 | DropTable("dbo.Tickets"); 109 | DropTable("dbo.AspNetUserRoles"); 110 | DropTable("dbo.AspNetRoles"); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /BeerPal/Project_Readme.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Your ASP.NET application 6 | 95 | 96 | 97 | 98 | 102 | 103 |
104 |
105 |

This application consists of:

106 |
    107 |
  • Sample pages showing basic nav between Home, About, and Contact
  • 108 |
  • Theming using Bootstrap
  • 109 |
  • Authentication, if selected, shows how to register and sign in
  • 110 |
  • ASP.NET features managed using NuGet
  • 111 |
112 |
113 | 114 | 131 | 132 |
133 |

Deploy

134 | 139 |
140 | 141 |
142 |

Get help

143 | 147 |
148 |
149 | 150 | 151 | -------------------------------------------------------------------------------- /BeerPal/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /BeerPal/Scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this),c="__jquery_unobtrusive_validation_form_reset";if(b.data(c))return;b.data(c,true);try{b.data("validator").resetForm()}finally{b.removeData(c)}b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){var d=a(b.form).find(":input").filter("[name='"+f(c)+"']");return d.is(":checkbox")?d.filter(":checked").val()||d.filter(":hidden").val()||"":d.is(":radio")?d.filter(":checked").val()||"":d.val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # ========================= 255 | # Operating System Files 256 | # ========================= 257 | 258 | # OSX 259 | # ========================= 260 | 261 | .DS_Store 262 | .AppleDouble 263 | .LSOverride 264 | 265 | # Thumbnails 266 | ._* 267 | 268 | # Files that might appear in the root of a volume 269 | .DocumentRevisions-V100 270 | .fseventsd 271 | .Spotlight-V100 272 | .TemporaryItems 273 | .Trashes 274 | .VolumeIcon.icns 275 | 276 | # Directories potentially created on remote AFP share 277 | .AppleDB 278 | .AppleDesktop 279 | Network Trash Folder 280 | Temporary Items 281 | .apdisk 282 | 283 | # Windows 284 | # ========================= 285 | 286 | # Windows image file caches 287 | Thumbs.db 288 | ehthumbs.db 289 | 290 | # Folder config file 291 | Desktop.ini 292 | 293 | # Recycle Bin used on file shares 294 | $RECYCLE.BIN/ 295 | 296 | # Windows Installer files 297 | *.cab 298 | *.msi 299 | *.msm 300 | *.msp 301 | 302 | # Windows shortcuts 303 | *.lnk 304 | -------------------------------------------------------------------------------- /BeerPal/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | BeerPal Brewery 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 93 | 94 | @RenderBody() 95 | 96 | 108 | 109 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /BeerPal/Controllers/SingleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using BeerPal.Entities; 7 | using BeerPal.Models; 8 | using BeerPal.Models.Single; 9 | using Microsoft.AspNet.Identity.Owin; 10 | using PayPal.Api; 11 | 12 | namespace BeerPal.Controllers 13 | { 14 | public class SingleController : Controller 15 | { 16 | private ApplicationDbContext _dbContext => HttpContext.GetOwinContext().Get(); 17 | 18 | public ActionResult Index() 19 | { 20 | var model = GetNextTourInfo(); 21 | 22 | return View(model); 23 | } 24 | 25 | [HttpPost] 26 | public ActionResult Index(IndexVm model) 27 | { 28 | if (ModelState.IsValid) 29 | { 30 | // Fetch the tour info from the server and NOT from the POST data. 31 | // Otherwise users could manipulate the data 32 | var tourInfo = GetNextTourInfo(); 33 | 34 | // Create a Ticket object to store info about the purchaser 35 | var ticket = new Ticket() 36 | { 37 | FirstName = model.FirstName, 38 | LastName = model.LastName, 39 | Email = model.Email, 40 | TourDate = tourInfo.TourDate 41 | }; 42 | _dbContext.Tickets.Add(ticket); 43 | _dbContext.SaveChanges(); 44 | 45 | // Get PayPal API Context using configuration from web.config 46 | var apiContext = GetApiContext(); 47 | 48 | // Create a new payment object 49 | var payment = new Payment 50 | { 51 | experience_profile_id = "XP-HMV5-V9ES-8QXN-2F33", // Created in the WebExperienceProfilesController. This one is for DigitalGoods. 52 | intent = "sale", 53 | payer = new Payer 54 | { 55 | payment_method = "paypal" 56 | }, 57 | transactions = new List 58 | { 59 | new Transaction 60 | { 61 | description = $"Brewery Tour (Single Payment) for {tourInfo.TourDate:dddd, dd MMMM yyyy}", 62 | amount = new Amount 63 | { 64 | currency = "USD", 65 | total = (tourInfo.Price/100M).ToString(), // PayPal expects string amounts, eg. "20.00" 66 | }, 67 | item_list = new ItemList() 68 | { 69 | items = new List() 70 | { 71 | new Item() 72 | { 73 | description = $"Brewery Tour (Single Payment) for {tourInfo.TourDate:dddd, dd MMMM yyyy}", 74 | currency = "USD", 75 | quantity = "1", 76 | price = (tourInfo.Price/100M).ToString(), // PayPal expects string amounts, eg. "20.00" 77 | } 78 | } 79 | } 80 | } 81 | }, 82 | redirect_urls = new RedirectUrls 83 | { 84 | return_url = Url.Action("Return", "Single", null, Request.Url.Scheme), 85 | cancel_url = Url.Action("Cancel", "Single", null, Request.Url.Scheme) 86 | } 87 | }; 88 | 89 | // Send the payment to PayPal 90 | var createdPayment = payment.Create(apiContext); 91 | 92 | // Save a reference to the paypal payment 93 | ticket.PayPalReference = createdPayment.id; 94 | _dbContext.SaveChanges(); 95 | 96 | // Find the Approval URL to send our user to 97 | var approvalUrl = 98 | createdPayment.links.FirstOrDefault( 99 | x => x.rel.Equals("approval_url", StringComparison.OrdinalIgnoreCase)); 100 | 101 | // Send the user to PayPal to approve the payment 102 | return Redirect(approvalUrl.href); 103 | } 104 | 105 | return View(model); 106 | } 107 | 108 | public ActionResult Return(string payerId, string paymentId) 109 | { 110 | // Fetch the existing ticket 111 | var ticket = _dbContext.Tickets.FirstOrDefault(x => x.PayPalReference == paymentId); 112 | 113 | // Get PayPal API Context using configuration from web.config 114 | var apiContext = GetApiContext(); 115 | 116 | // Set the payer for the payment 117 | var paymentExecution = new PaymentExecution() 118 | { 119 | payer_id = payerId 120 | }; 121 | 122 | // Identify the payment to execute 123 | var payment = new Payment() 124 | { 125 | id = paymentId 126 | }; 127 | 128 | // Execute the Payment 129 | var executedPayment = payment.Execute(apiContext, paymentExecution); 130 | 131 | return RedirectToAction("Thankyou"); 132 | } 133 | 134 | public ActionResult Cancel() 135 | { 136 | return View(); 137 | } 138 | 139 | public ActionResult ThankYou() 140 | { 141 | return View(); 142 | } 143 | 144 | private IndexVm GetNextTourInfo() 145 | { 146 | return new IndexVm() 147 | { 148 | // Always set tour for tomorrow 149 | TourDate = DateTime.Today.AddDays(1), 150 | // Represent price in cents to avoid rounding errors 151 | Price = 2000 152 | }; 153 | } 154 | 155 | private APIContext GetApiContext() 156 | { 157 | // Authenticate with PayPal 158 | var config = ConfigManager.Instance.GetProperties(); 159 | var accessToken = new OAuthTokenCredential(config).GetAccessToken(); 160 | var apiContext = new APIContext(accessToken); 161 | return apiContext; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /BeerPal/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model BeerPal.Models.Home.IndexVm 2 | @{ 3 | ViewBag.Title = "Home Page"; 4 | } 5 | 6 |
    7 | 30 | 31 |
    32 | 33 | 74 | 75 |
    76 |
    77 |
    78 |
    79 | 80 |
    81 |
    82 |

    Brewery Tour (Single Payment)

    83 |

    Come along for a great tour of our brewery.

    84 |

    Next Event

    85 |
      86 |
    • @(DateTime.Today.AddDays(1).ToString("dddd, dd MMMM yyyy"))
    • 87 |
    88 | Buy Ticket Now ($20) 89 |
    90 |
    91 |
    92 |
    93 | 94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    Online Beer Shopping (E-Commerce)

    100 |

    The best of the beers most wanted in @DateTime.Today.Year

    101 |
    102 |
    103 |
    104 |
    105 | @foreach (var beer in Model.Beers) 106 | { 107 |
    108 | @using (Html.BeginForm("Add", "ECommerce", FormMethod.Post)) 109 | { 110 | @Html.Hidden("BeerId", beer.Id) 111 |
    112 |
    113 |
    114 |

    @beer.Name

    115 | $@((beer.Price/100M).ToString("0.00")) 116 |
    117 | 118 | 120 |
    121 | } 122 |
    123 | } 124 |
    125 |
    126 |
    127 | 128 |
    129 |
    130 |
    131 |
    132 |
    133 |

    Beer Delivery Subscription (Subscription Billing)

    134 |

    Beer delivered to your door, can it get any better?

    135 |
    136 |
    137 |
    138 |
    139 | @foreach (var plan in Model.Plans) 140 | { 141 |
    142 |
      143 |
    • @plan.Name
    • 144 |
    • $ @((plan.Price / 100M).ToString("0.00")) 145 |

      Monthly

      146 |

    • 147 |
    • @plan.NumberOfBeers Beer@(plan.NumberOfBeers != 1 ? "s" : "")
    • 148 |
    • @plan.Description1
    • 149 |
    • @plan.Description2
    • 150 |
    • Sign Up
    • 151 |
    152 |
    153 | } 154 |
    155 |
    156 |
    157 | 158 | 159 | --------------------------------------------------------------------------------