FEATURE: Capture cardholder address fields for Stripe customer (#161)
- Adds the following fields to the subscription payment form: - Cardholder Name - Country - Postal Code - Address Line 1 - City - State or Province - Stripe recommends Cardholder Name & Country for verification; Cardholder Name, Country, and State/Province for US/Canada selections are required fields - All fields are passed to Stripe for verification on submit - Fields are also captured on the customer record in Stripe, under Billing Details
This commit is contained in:
parent
2babb43ffb
commit
803bba7938
|
@ -57,7 +57,12 @@ module DiscourseSubscriptions
|
|||
def create
|
||||
params.require(%i[source plan])
|
||||
begin
|
||||
customer = find_or_create_customer(params[:source])
|
||||
customer =
|
||||
find_or_create_customer(
|
||||
params[:source],
|
||||
params[:cardholder_name],
|
||||
params[:cardholder_address],
|
||||
)
|
||||
plan = ::Stripe::Price.retrieve(params[:plan])
|
||||
|
||||
if params[:promo].present?
|
||||
|
@ -170,13 +175,32 @@ module DiscourseSubscriptions
|
|||
.sort_by { |plan| plan[:amount] }
|
||||
end
|
||||
|
||||
def find_or_create_customer(source)
|
||||
def find_or_create_customer(source, cardholder_name = nil, cardholder_address = nil)
|
||||
customer = Customer.find_by_user_id(current_user.id)
|
||||
cardholder_address =
|
||||
(
|
||||
if cardholder_address.present?
|
||||
{
|
||||
line1: cardholder_address[:line1],
|
||||
city: cardholder_address[:city],
|
||||
state: cardholder_address[:state],
|
||||
country: cardholder_address[:country],
|
||||
postal_code: cardholder_address[:postalCode],
|
||||
}
|
||||
else
|
||||
nil
|
||||
end
|
||||
)
|
||||
|
||||
if customer.present?
|
||||
::Stripe::Customer.retrieve(customer.customer_id)
|
||||
else
|
||||
::Stripe::Customer.create(email: current_user.email, source: source)
|
||||
::Stripe::Customer.create(
|
||||
email: current_user.email,
|
||||
source: source,
|
||||
name: cardholder_name,
|
||||
address: cardholder_address,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
import { computed } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["subscribe-ca-province-select"],
|
||||
classNames: ["subscribe-address-state-select"],
|
||||
nameProperty: "name",
|
||||
valueProperty: "value",
|
||||
|
||||
selectKitOptions: {
|
||||
filterable: true,
|
||||
allowAny: false,
|
||||
translatedNone: I18n.t(
|
||||
"discourse_subscriptions.subscribe.cardholder_address.province"
|
||||
),
|
||||
},
|
||||
|
||||
content: computed(function () {
|
||||
return [
|
||||
["AB", "Alberta"],
|
||||
["BC", "British Columbia"],
|
||||
["MB", "Manitoba"],
|
||||
["NB", "New Brunswick"],
|
||||
["NL", "Newfoundland and Labrador"],
|
||||
["NT", "Northwest Territories"],
|
||||
["NS", "Nova Scotia"],
|
||||
["NU", "Nunavut"],
|
||||
["ON", "Ontario"],
|
||||
["PE", "Prince Edward Island"],
|
||||
["QC", "Quebec"],
|
||||
["SK", "Saskatchewan"],
|
||||
["YT", "Yukon"],
|
||||
].map((arr) => {
|
||||
return { value: arr[0], name: arr[1] };
|
||||
});
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,274 @@
|
|||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
import { computed } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["subscribe-country-select"],
|
||||
classNames: ["subscribe-address-country-select"],
|
||||
nameProperty: "name",
|
||||
valueProperty: "value",
|
||||
|
||||
selectKitOptions: {
|
||||
filterable: true,
|
||||
allowAny: false,
|
||||
translatedNone: I18n.t(
|
||||
"discourse_subscriptions.subscribe.cardholder_address.country"
|
||||
),
|
||||
},
|
||||
|
||||
content: computed(function () {
|
||||
return [
|
||||
["AF", "Afghanistan"],
|
||||
["AX", "Åland Islands"],
|
||||
["AL", "Albania"],
|
||||
["DZ", "Algeria"],
|
||||
["AS", "American Samoa"],
|
||||
["AD", "Andorra"],
|
||||
["AO", "Angola"],
|
||||
["AI", "Anguilla"],
|
||||
["AQ", "Antarctica"],
|
||||
["AG", "Antigua and Barbuda"],
|
||||
["AR", "Argentina"],
|
||||
["AM", "Armenia"],
|
||||
["AW", "Aruba"],
|
||||
["AU", "Australia"],
|
||||
["AT", "Austria"],
|
||||
["AZ", "Azerbaijan"],
|
||||
["BS", "Bahamas"],
|
||||
["BH", "Bahrain"],
|
||||
["BD", "Bangladesh"],
|
||||
["BB", "Barbados"],
|
||||
["BY", "Belarus"],
|
||||
["BE", "Belgium"],
|
||||
["BZ", "Belize"],
|
||||
["BJ", "Benin"],
|
||||
["BM", "Bermuda"],
|
||||
["BT", "Bhutan"],
|
||||
["BO", "Bolivia, Plurinational State of"],
|
||||
["BQ", "Bonaire, Sint Eustatius and Saba"],
|
||||
["BA", "Bosnia and Herzegovina"],
|
||||
["BW", "Botswana"],
|
||||
["BV", "Bouvet Island"],
|
||||
["BR", "Brazil"],
|
||||
["IO", "British Indian Ocean Territory"],
|
||||
["BN", "Brunei Darussalam"],
|
||||
["BG", "Bulgaria"],
|
||||
["BF", "Burkina Faso"],
|
||||
["BI", "Burundi"],
|
||||
["KH", "Cambodia"],
|
||||
["CM", "Cameroon"],
|
||||
["CA", "Canada"],
|
||||
["CV", "Cape Verde"],
|
||||
["KY", "Cayman Islands"],
|
||||
["CF", "Central African Republic"],
|
||||
["TD", "Chad"],
|
||||
["CL", "Chile"],
|
||||
["CN", "China"],
|
||||
["CX", "Christmas Island"],
|
||||
["CC", "Cocos (Keeling) Islands"],
|
||||
["CO", "Colombia"],
|
||||
["KM", "Comoros"],
|
||||
["CG", "Congo"],
|
||||
["CD", "Congo, the Democratic Republic of the"],
|
||||
["CK", "Cook Islands"],
|
||||
["CR", "Costa Rica"],
|
||||
["CI", "Côte d'Ivoire"],
|
||||
["HR", "Croatia"],
|
||||
["CU", "Cuba"],
|
||||
["CW", "Curaçao"],
|
||||
["CY", "Cyprus"],
|
||||
["CZ", "Czech Republic"],
|
||||
["DK", "Denmark"],
|
||||
["DJ", "Djibouti"],
|
||||
["DM", "Dominica"],
|
||||
["DO", "Dominican Republic"],
|
||||
["EC", "Ecuador"],
|
||||
["EG", "Egypt"],
|
||||
["SV", "El Salvador"],
|
||||
["GQ", "Equatorial Guinea"],
|
||||
["ER", "Eritrea"],
|
||||
["EE", "Estonia"],
|
||||
["ET", "Ethiopia"],
|
||||
["FK", "Falkland Islands (Malvinas)"],
|
||||
["FO", "Faroe Islands"],
|
||||
["FJ", "Fiji"],
|
||||
["FI", "Finland"],
|
||||
["FR", "France"],
|
||||
["GF", "French Guiana"],
|
||||
["PF", "French Polynesia"],
|
||||
["TF", "French Southern Territories"],
|
||||
["GA", "Gabon"],
|
||||
["GM", "Gambia"],
|
||||
["GE", "Georgia"],
|
||||
["DE", "Germany"],
|
||||
["GH", "Ghana"],
|
||||
["GI", "Gibraltar"],
|
||||
["GR", "Greece"],
|
||||
["GL", "Greenland"],
|
||||
["GD", "Grenada"],
|
||||
["GP", "Guadeloupe"],
|
||||
["GU", "Guam"],
|
||||
["GT", "Guatemala"],
|
||||
["GG", "Guernsey"],
|
||||
["GN", "Guinea"],
|
||||
["GW", "Guinea-Bissau"],
|
||||
["GY", "Guyana"],
|
||||
["HT", "Haiti"],
|
||||
["HM", "Heard Island and McDonald Islands"],
|
||||
["VA", "Holy See (Vatican City State)"],
|
||||
["HN", "Honduras"],
|
||||
["HK", "Hong Kong"],
|
||||
["HU", "Hungary"],
|
||||
["IS", "Iceland"],
|
||||
["IN", "India"],
|
||||
["ID", "Indonesia"],
|
||||
["IR", "Iran, Islamic Republic of"],
|
||||
["IQ", "Iraq"],
|
||||
["IE", "Ireland"],
|
||||
["IM", "Isle of Man"],
|
||||
["IL", "Israel"],
|
||||
["IT", "Italy"],
|
||||
["JM", "Jamaica"],
|
||||
["JP", "Japan"],
|
||||
["JE", "Jersey"],
|
||||
["JO", "Jordan"],
|
||||
["KZ", "Kazakhstan"],
|
||||
["KE", "Kenya"],
|
||||
["KI", "Kiribati"],
|
||||
["KP", "Korea, Democratic People's Republic of"],
|
||||
["KR", "Korea, Republic of"],
|
||||
["KW", "Kuwait"],
|
||||
["KG", "Kyrgyzstan"],
|
||||
["LA", "Lao People's Democratic Republic"],
|
||||
["LV", "Latvia"],
|
||||
["LB", "Lebanon"],
|
||||
["LS", "Lesotho"],
|
||||
["LR", "Liberia"],
|
||||
["LY", "Libya"],
|
||||
["LI", "Liechtenstein"],
|
||||
["LT", "Lithuania"],
|
||||
["LU", "Luxembourg"],
|
||||
["MO", "Macao"],
|
||||
["MK", "Macedonia, the former Yugoslav Republic of"],
|
||||
["MG", "Madagascar"],
|
||||
["MW", "Malawi"],
|
||||
["MY", "Malaysia"],
|
||||
["MV", "Maldives"],
|
||||
["ML", "Mali"],
|
||||
["MT", "Malta"],
|
||||
["MH", "Marshall Islands"],
|
||||
["MQ", "Martinique"],
|
||||
["MR", "Mauritania"],
|
||||
["MU", "Mauritius"],
|
||||
["YT", "Mayotte"],
|
||||
["MX", "Mexico"],
|
||||
["FM", "Micronesia, Federated States of"],
|
||||
["MD", "Moldova, Republic of"],
|
||||
["MC", "Monaco"],
|
||||
["MN", "Mongolia"],
|
||||
["ME", "Montenegro"],
|
||||
["MS", "Montserrat"],
|
||||
["MA", "Morocco"],
|
||||
["MZ", "Mozambique"],
|
||||
["MM", "Myanmar"],
|
||||
["NA", "Namibia"],
|
||||
["NR", "Nauru"],
|
||||
["NP", "Nepal"],
|
||||
["NL", "Netherlands"],
|
||||
["NC", "New Caledonia"],
|
||||
["NZ", "New Zealand"],
|
||||
["NI", "Nicaragua"],
|
||||
["NE", "Niger"],
|
||||
["NG", "Nigeria"],
|
||||
["NU", "Niue"],
|
||||
["NF", "Norfolk Island"],
|
||||
["MP", "Northern Mariana Islands"],
|
||||
["NO", "Norway"],
|
||||
["OM", "Oman"],
|
||||
["PK", "Pakistan"],
|
||||
["PW", "Palau"],
|
||||
["PS", "Palestinian Territory, Occupied"],
|
||||
["PA", "Panama"],
|
||||
["PG", "Papua New Guinea"],
|
||||
["PY", "Paraguay"],
|
||||
["PE", "Peru"],
|
||||
["PH", "Philippines"],
|
||||
["PN", "Pitcairn"],
|
||||
["PL", "Poland"],
|
||||
["PT", "Portugal"],
|
||||
["PR", "Puerto Rico"],
|
||||
["QA", "Qatar"],
|
||||
["RE", "Réunion"],
|
||||
["RO", "Romania"],
|
||||
["RU", "Russian Federation"],
|
||||
["RW", "Rwanda"],
|
||||
["BL", "Saint Barthélemy"],
|
||||
["SH", "Saint Helena, Ascension and Tristan da Cunha"],
|
||||
["KN", "Saint Kitts and Nevis"],
|
||||
["LC", "Saint Lucia"],
|
||||
["MF", "Saint Martin (French part)"],
|
||||
["PM", "Saint Pierre and Miquelon"],
|
||||
["VC", "Saint Vincent and the Grenadines"],
|
||||
["WS", "Samoa"],
|
||||
["SM", "San Marino"],
|
||||
["ST", "Sao Tome and Principe"],
|
||||
["SA", "Saudi Arabia"],
|
||||
["SN", "Senegal"],
|
||||
["RS", "Serbia"],
|
||||
["SC", "Seychelles"],
|
||||
["SL", "Sierra Leone"],
|
||||
["SG", "Singapore"],
|
||||
["SX", "Sint Maarten (Dutch part)"],
|
||||
["SK", "Slovakia"],
|
||||
["SI", "Slovenia"],
|
||||
["SB", "Solomon Islands"],
|
||||
["SO", "Somalia"],
|
||||
["ZA", "South Africa"],
|
||||
["GS", "South Georgia and the South Sandwich Islands"],
|
||||
["SS", "South Sudan"],
|
||||
["ES", "Spain"],
|
||||
["LK", "Sri Lanka"],
|
||||
["SD", "Sudan"],
|
||||
["SR", "Suriname"],
|
||||
["SJ", "Svalbard and Jan Mayen"],
|
||||
["SZ", "Swaziland"],
|
||||
["SE", "Sweden"],
|
||||
["CH", "Switzerland"],
|
||||
["SY", "Syrian Arab Republic"],
|
||||
["TW", "Taiwan, Province of China"],
|
||||
["TJ", "Tajikistan"],
|
||||
["TZ", "Tanzania, United Republic of"],
|
||||
["TH", "Thailand"],
|
||||
["TL", "Timor-Leste"],
|
||||
["TG", "Togo"],
|
||||
["TK", "Tokelau"],
|
||||
["TO", "Tonga"],
|
||||
["TT", "Trinidad and Tobago"],
|
||||
["TN", "Tunisia"],
|
||||
["TR", "Turkey"],
|
||||
["TM", "Turkmenistan"],
|
||||
["TC", "Turks and Caicos Islands"],
|
||||
["TV", "Tuvalu"],
|
||||
["UG", "Uganda"],
|
||||
["UA", "Ukraine"],
|
||||
["AE", "United Arab Emirates"],
|
||||
["GB", "United Kingdom"],
|
||||
["US", "United States"],
|
||||
["UM", "United States Minor Outlying Islands"],
|
||||
["UY", "Uruguay"],
|
||||
["UZ", "Uzbekistan"],
|
||||
["VU", "Vanuatu"],
|
||||
["VE", "Venezuela, Bolivarian Republic of"],
|
||||
["VN", "Viet Nam"],
|
||||
["VG", "Virgin Islands, British"],
|
||||
["VI", "Virgin Islands, U.S."],
|
||||
["WF", "Wallis and Futuna"],
|
||||
["EH", "Western Sahara"],
|
||||
["YE", "Yemen"],
|
||||
["ZM", "Zambia"],
|
||||
["ZW", "Zimbabwe"],
|
||||
].map((arr) => {
|
||||
return { value: arr[0], name: arr[1] };
|
||||
});
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,88 @@
|
|||
import ComboBoxComponent from "select-kit/components/combo-box";
|
||||
import { computed } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
pluginApiIdentifiers: ["subscribe-us-state-select"],
|
||||
classNames: ["subscribe-address-state-select"],
|
||||
nameProperty: "name",
|
||||
valueProperty: "value",
|
||||
|
||||
selectKitOptions: {
|
||||
filterable: true,
|
||||
allowAny: false,
|
||||
translatedNone: I18n.t(
|
||||
"discourse_subscriptions.subscribe.cardholder_address.state"
|
||||
),
|
||||
},
|
||||
|
||||
content: computed(function () {
|
||||
return [
|
||||
["AL", "Alabama"],
|
||||
["AK", "Alaska"],
|
||||
["AZ", "Arizona"],
|
||||
["AR", "Arkansas"],
|
||||
["CA", "California"],
|
||||
["CO", "Colorado"],
|
||||
["CT", "Connecticut"],
|
||||
["DE", "Delaware"],
|
||||
["US", "District"],
|
||||
["FL", "Florida"],
|
||||
["GA", "Georgia"],
|
||||
["HI", "Hawaii"],
|
||||
["ID", "Idaho"],
|
||||
["IL", "Illinois"],
|
||||
["IN", "Indiana"],
|
||||
["IA", "Iowa"],
|
||||
["KS", "Kansas"],
|
||||
["KY", "Kentucky"],
|
||||
["LA", "Louisiana"],
|
||||
["ME", "Maine"],
|
||||
["MD", "Maryland"],
|
||||
["MA", "Massachusetts"],
|
||||
["MI", "Michigan"],
|
||||
["MN", "Minnesota"],
|
||||
["MS", "Mississippi"],
|
||||
["MO", "Missouri"],
|
||||
["MT", "Montana"],
|
||||
["NE", "Nebraska"],
|
||||
["NV", "Nevada"],
|
||||
["NH", "New Hampshire"],
|
||||
["NJ", "New Jersey"],
|
||||
["NM", "New Mexico"],
|
||||
["NY", "New York"],
|
||||
["NC", "North Carolina"],
|
||||
["ND", "North Dakota"],
|
||||
["OH", "Ohio"],
|
||||
["OK", "Oklahoma"],
|
||||
["OR", "Oregon"],
|
||||
["PA", "Pennsylvania"],
|
||||
["RI", "Rhode"],
|
||||
["SC", "South"],
|
||||
["SD", "South"],
|
||||
["TN", "Tennessee"],
|
||||
["TX", "Texas"],
|
||||
["UT", "Utah"],
|
||||
["VT", "Vermont"],
|
||||
["VA", "Virginia"],
|
||||
["WA", "Washington"],
|
||||
["WV", "West"],
|
||||
["WI", "Wisconsin"],
|
||||
["WY", "Wyoming"],
|
||||
["AS", "American Samoa"],
|
||||
["GU", "Guam"],
|
||||
["MP", "Northern Mariana Islands"],
|
||||
["PR", "Puerto Rico"],
|
||||
["VI", "U.S. Virgin Islands"],
|
||||
["UM", "U.S. Minor Outlying Islands"],
|
||||
["MH", "Marshall Islands"],
|
||||
["FM", "Micronesia"],
|
||||
["PW", "Palau"],
|
||||
["AA", "U.S. Armed Forces – Americas"],
|
||||
["AE", "U.S. Armed Forces – Europe"],
|
||||
["AP", "U.S. Armed Forces – Pacific"],
|
||||
].map((arr) => {
|
||||
return { value: arr[0], name: arr[1] };
|
||||
});
|
||||
}),
|
||||
});
|
|
@ -10,7 +10,17 @@ export default Controller.extend({
|
|||
dialog: service(),
|
||||
selectedPlan: null,
|
||||
promoCode: null,
|
||||
cardholderName: null,
|
||||
cardholderAddress: {
|
||||
line1: null,
|
||||
city: null,
|
||||
state: null,
|
||||
country: null,
|
||||
postalCode: null,
|
||||
},
|
||||
isAnonymous: not("currentUser"),
|
||||
isCountryUS: false,
|
||||
isCountryCA: false,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
@ -21,6 +31,9 @@ export default Controller.extend({
|
|||
const elements = this.get("stripe").elements();
|
||||
|
||||
this.set("cardElement", elements.create("card", { hidePostalCode: true }));
|
||||
|
||||
this.set("isCountryUS", this.cardholderAddress.country === "US");
|
||||
this.set("isCountryCA", this.cardholderAddress.country === "CA");
|
||||
},
|
||||
|
||||
alert(path) {
|
||||
|
@ -37,20 +50,31 @@ export default Controller.extend({
|
|||
},
|
||||
|
||||
createSubscription(plan) {
|
||||
return this.stripe.createToken(this.get("cardElement")).then((result) => {
|
||||
if (result.error) {
|
||||
this.set("loading", false);
|
||||
return result;
|
||||
} else {
|
||||
const subscription = Subscription.create({
|
||||
source: result.token.id,
|
||||
plan: plan.get("id"),
|
||||
promo: this.promoCode,
|
||||
});
|
||||
return this.stripe
|
||||
.createToken(this.get("cardElement"), {
|
||||
name: this.cardholderName, // Recommended by Stripe
|
||||
address_line1: this.cardholderAddress.line1 ?? "",
|
||||
address_city: this.cardholderAddress.city ?? "",
|
||||
address_state: this.cardholderAddress.state ?? "",
|
||||
address_zip: this.cardholderAddress.postalCode ?? "",
|
||||
address_country: this.cardholderAddress.country, // Recommended by Stripe
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.error) {
|
||||
this.set("loading", false);
|
||||
return result;
|
||||
} else {
|
||||
const subscription = Subscription.create({
|
||||
source: result.token.id,
|
||||
plan: plan.get("id"),
|
||||
promo: this.promoCode,
|
||||
cardholderName: this.cardholderName,
|
||||
cardholderAddress: this.cardholderAddress,
|
||||
});
|
||||
|
||||
return subscription.save();
|
||||
}
|
||||
});
|
||||
return subscription.save();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleAuthentication(plan, transaction) {
|
||||
|
@ -83,11 +107,23 @@ export default Controller.extend({
|
|||
},
|
||||
|
||||
actions: {
|
||||
changeCountry(country) {
|
||||
this.set("cardholderAddress.country", country);
|
||||
this.set("isCountryUS", country === "US");
|
||||
this.set("isCountryCA", country === "CA");
|
||||
},
|
||||
|
||||
changeState(stateOrProvince) {
|
||||
this.set("cardholderAddress.state", stateOrProvince);
|
||||
},
|
||||
|
||||
stripePaymentHandler() {
|
||||
this.set("loading", true);
|
||||
const plan = this.get("model.plans")
|
||||
.filterBy("id", this.selectedPlan)
|
||||
.get("firstObject");
|
||||
const cardholderAddress = this.cardholderAddress;
|
||||
const cardholderName = this.cardholderName;
|
||||
|
||||
if (!plan) {
|
||||
this.alert("plans.validate.payment_options.required");
|
||||
|
@ -95,6 +131,30 @@ export default Controller.extend({
|
|||
return;
|
||||
}
|
||||
|
||||
if (!cardholderName) {
|
||||
this.alert("subscribe.invalid_cardholder_name");
|
||||
this.set("loading", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cardholderAddress.country) {
|
||||
this.alert("subscribe.invalid_cardholder_country");
|
||||
this.set("loading", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cardholderAddress.country === "US" && !cardholderAddress.state) {
|
||||
this.alert("subscribe.invalid_cardholder_state");
|
||||
this.set("loading", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cardholderAddress.country === "CA" && !cardholderAddress.state) {
|
||||
this.alert("subscribe.invalid_cardholder_province");
|
||||
this.set("loading", false);
|
||||
return;
|
||||
}
|
||||
|
||||
let transaction = this.createSubscription(plan);
|
||||
|
||||
transaction
|
||||
|
|
|
@ -13,6 +13,8 @@ const Subscription = EmberObject.extend({
|
|||
source: this.source,
|
||||
plan: this.plan,
|
||||
promo: this.promo,
|
||||
cardholder_name: this.cardholderName,
|
||||
cardholder_address: this.cardholderAddress,
|
||||
};
|
||||
|
||||
return ajax("/s/create", { method: "post", data });
|
||||
|
|
|
@ -30,6 +30,72 @@
|
|||
{{else if isAnonymous}}
|
||||
{{login-required}}
|
||||
{{else}}
|
||||
<Input
|
||||
@type="text"
|
||||
name="cardholder_name"
|
||||
placeholder={{i18n
|
||||
"discourse_subscriptions.subscribe.cardholder_name"
|
||||
}}
|
||||
@value={{cardholderName}}
|
||||
class="subscribe-name"
|
||||
/>
|
||||
<div class="address-fields">
|
||||
{{subscribe-country-select
|
||||
value=cardholderAddress.country
|
||||
onChange=(action "changeCountry")
|
||||
}}
|
||||
<Input
|
||||
@type="text"
|
||||
name="cardholder_postal_code"
|
||||
placeholder={{i18n
|
||||
"discourse_subscriptions.subscribe.cardholder_address.postal_code"
|
||||
}}
|
||||
@value={{cardholderAddress.postalCode}}
|
||||
class="subscribe-address-postal-code"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
@type="text"
|
||||
name="cardholder_line1"
|
||||
placeholder={{i18n
|
||||
"discourse_subscriptions.subscribe.cardholder_address.line1"
|
||||
}}
|
||||
@value={{cardholderAddress.line1}}
|
||||
class="subscribe-address-line1"
|
||||
/>
|
||||
<div class="address-fields">
|
||||
<Input
|
||||
@type="text"
|
||||
name="cardholder_city"
|
||||
placeholder={{i18n
|
||||
"discourse_subscriptions.subscribe.cardholder_address.city"
|
||||
}}
|
||||
@value={{cardholderAddress.city}}
|
||||
class="subscribe-address-city"
|
||||
/>
|
||||
{{#if isCountryUS}}
|
||||
{{subscribe-us-state-select
|
||||
value=cardholderAddress.state
|
||||
onChange=(action "changeState")
|
||||
}}
|
||||
{{else if isCountryCA}}
|
||||
{{subscribe-ca-province-select
|
||||
value=cardholderAddress.state
|
||||
onChange=(action "changeState")
|
||||
}}
|
||||
{{else}}
|
||||
<Input
|
||||
@type="text"
|
||||
name="cardholder_state"
|
||||
placeholder={{i18n
|
||||
"discourse_subscriptions.subscribe.cardholder_address.state"
|
||||
}}
|
||||
@value={{cardholderAddress.state}}
|
||||
class="subscribe-address-state"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<Input
|
||||
@type="text"
|
||||
name="promo_code"
|
||||
|
|
|
@ -49,6 +49,29 @@
|
|||
color: var(--quaternary);
|
||||
}
|
||||
|
||||
.subscribe-promo-code {
|
||||
.subscribe-promo-code,
|
||||
.subscribe-name,
|
||||
.subscribe-address-line1,
|
||||
.subscribe-address-city,
|
||||
.subscribe-address-state,
|
||||
.subscribe-address-country-select,
|
||||
.subscribe-address-state-select,
|
||||
.subscribe-address-postal-code {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.subscribe-address-country-select,
|
||||
.subscribe-address-state-select {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
@media all and (min-width: 1350px) {
|
||||
.address-fields {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
& > input,
|
||||
& > .select-kit {
|
||||
width: calc(50% - 4.5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,10 @@ en:
|
|||
no_products: There are currently no products available.
|
||||
unauthenticated: Log in or create an account to subscribe.
|
||||
invalid_coupon: You entered an invalid coupon code. Please try again.
|
||||
invalid_cardholder_name: Cardholder name is required.
|
||||
invalid_cardholder_country: Country is required.
|
||||
invalid_cardholder_state: State is required.
|
||||
invalid_cardholder_province: Province is required.
|
||||
card:
|
||||
title: Payment
|
||||
customer:
|
||||
|
@ -115,6 +119,14 @@ en:
|
|||
purchased: Purchased
|
||||
go_to_billing: Go to Billing
|
||||
already_purchased: Thanks so much for your prior purchase of this product!
|
||||
cardholder_name: Cardholder Name
|
||||
cardholder_address:
|
||||
line1: Street Address
|
||||
city: City
|
||||
state: State
|
||||
province: Province
|
||||
country: Country
|
||||
postal_code: Postal Code
|
||||
billing:
|
||||
name: Full name
|
||||
email: Email
|
||||
|
|
|
@ -313,6 +313,31 @@ module DiscourseSubscriptions
|
|||
}.not_to change { DiscourseSubscriptions::Customer.count }
|
||||
end
|
||||
|
||||
context "with customer name & address" do
|
||||
it "creates a customer & subscription when a customer address is provided" do
|
||||
::Stripe::Price.expects(:retrieve).returns(type: "recurring", metadata: {})
|
||||
::Stripe::Subscription.expects(:create).returns(
|
||||
status: "active",
|
||||
customer: "cus_1234",
|
||||
)
|
||||
expect {
|
||||
post "/s/create.json",
|
||||
params: {
|
||||
plan: "plan_1234",
|
||||
source: "tok_1234",
|
||||
cardholder_name: "A. Customer",
|
||||
cardholder_address: {
|
||||
line1: "123 Main Street",
|
||||
city: "Anywhere",
|
||||
state: "VT",
|
||||
country: "US",
|
||||
postal_code: "12345",
|
||||
},
|
||||
}
|
||||
}.to change { DiscourseSubscriptions::Customer.count }
|
||||
end
|
||||
end
|
||||
|
||||
context "with promo code" do
|
||||
context "with invalid code" do
|
||||
it "prevents use of invalid coupon codes" do
|
||||
|
|
Loading…
Reference in New Issue