Multiple Plans with Stripe on Rails & No Customer Payment Source - stripe-payments

I have Stripe setup with Rails and Devise. It works beautifully, but there is three errors.
First of all, I would like to implement two different plans. Once they select it with the Pay With Stripe button, Stripe would do the work of making the customer pay the plan they choose monthly.
Second issue: When I go to the /charges/new path, it works beautifully and shows the plans. However, when I go to the /charges path, it gives me the error of This customer has no attached payment source.
Third issue: The user is suppose to go from /users/new to the /charges/new. This isn't happening.
How would I fix all of these errors?
Here's my charges_controller.rb:
class ChargesController < ApplicationController
before_filter :authenticate_user!
def new
end
def update
end
def create
token = params[:stripeToken]
customer = Stripe::Customer.create(
source: token,
plan: 2020,
email: current_user.email,
)
current_user.subscribed = true
current_user.stripeid = customer.id
current_user.save
redirect_to profile_path
end
end
Here's my routes.rb file:
Rails.application.routes.draw do
devise_for :users, controllers: {registrations: "registrations"}, path_names: { sign_in: "login", sign_out: "logout", registration: "signup", sign_up: "cmon_lemme_in_guys" }
resources :things
resources :charges, only: [:new, :create]
get "charges", to: "charges#create"
authenticated :user do
root "things#profile"
get "profile", to: "things#profile"
end
root "index#welcome"
get "license/terms"
end

Related

Stripe Connect: limit the number of steps during the account linking onboarding

I'm testing Stripe Connect to allow users to pay other users. I'm testing it with the Stripe Java library (in Kotlin).
As the documentation suggest I need the users that will receive money to create a Stripe account with the Account.create API, and then to link the account with the AccountLink.create API.
When the user go through the Stripe onboarding flow (the Stripe url received by the AccountLink.create API) they are required to input a lot of data in many steps.
This steps are:
01: "Get started with Stripe": user is asked to enater the email
02: "Create your free Stripe account": user need to enter email and password
03: "Secure your account with a mobile number": enable the 2FA
04: "Let’s start with some basics": user is asket to enter the Business location and the Type of business
05: "Verify your personal details": user is asked to enter Name, Address, Email address and Phone number
06: "Professional details": the user have to enter VAT number (optional) and select an Industry
07: "Add your bank to receive payouts": the user have to enter Currency and Bank details
08: "Add public details for customers": the user have to enter the "Statement descriptor", "Shortened descriptor" and the "Customer support phone number"
09: "Show customers your climate commitment": the user need to select, or skip, a climate contribution
10: "Review and finish up": the user have to review and confirm the data
I would like to only keep steps 1, 2, 3, 5 (without address and phone number), 7 and 10 during the initial onboarding, and remove the other steps, to decrease friction.
I saw other websites using Stripe Connect, and I know for a fact that it is possible.
This is the code I'm using:
Account.create request:
suspend fun stripeCreateNewAccount(
email: String,
localID: String,
productDescription: String,
onSuccess: suspend (Account?) -> Unit,
) {
try {
Stripe.apiKey = Constants.STRIPE_TOKEN_TEST
val metadata: MutableMap<String, String> = HashMap()
metadata["id"] = localID
val createAccountParams = AccountCreateParams.Builder()
.setType(AccountCreateParams.Type.STANDARD)
.setEmail(email.lowercase().trim())
.setMetadata(metadata)
.setBusinessType(AccountCreateParams.BusinessType.INDIVIDUAL)
.setBusinessProfile(
AccountCreateParams.BusinessProfile.builder()
.setUrl(url)
.setName("Testing Stripe connect")
.setProductDescription(productDescription)
.build()
)
.setCompany(
AccountCreateParams.Company.Builder()
.setName("Test Company Name")
.build()
)
.setIndividual(
AccountCreateParams.Individual.Builder()
.setFirstName("John")
.setLastName("Smith")
.setEmail(email.lowercase().trim())
.build()
)
.setBusinessProfile(
AccountCreateParams.BusinessProfile.builder()
.setUrl("${Constants.BASE_URL}/list/$localID" )
.setProductDescription(productDescription)
.build()
)
.build()
onSuccess(Account.create(createAccountParams))
} catch (e: Exception) {
e.printStackTrace()
onSuccess(null)
}
}
and this is the AccountLink.create request:
suspend fun stripeLinkExistingAccount(
idAccountToLink: String,
onCompleted: suspend (String?) -> Unit,
) {
try {
Stripe.apiKey = Constants.STRIPE_TOKEN_TEST
val params = AccountLinkCreateParams
.builder()
.setAccount(idAccountToLink)
.setRefreshUrl("${Constants.BASE_URL}/profile")
.setReturnUrl("${Constants.BASE_URL}/profile")
.setType(AccountLinkCreateParams.Type.ACCOUNT_ONBOARDING)
.build()
AccountLink.create(params)?.let { accountLink ->
onCompleted(accountLink.url) // The Stripe onboarding url
} ?: run {
// Something went wrong
onCompleted(null)
}
} catch (e: Exception) {
e.printStackTrace()
onCompleted(null)
}
}
Unfortunately, this is not something you can control. You are creating a Standard account which means the person signs up directly with Stripe and opens a "full account" with them. Stripe will need to collect all the bits of information they require for identity verification, but they will also collect additional details they need or want to offer (like the Climate screen). You can't skip this.
One potential alternative would be to switch to Express accounts instead for example where you control more of the onboarding. But it comes with different risks and restrictions as covered in the docs here.
Even with Express, you are NOT going to accomplish what you want. By Law and Regulations, Stripe MUST follow KYC (Know Your Customer) and AML (Anti-Money Laundering) identification and verification for ANY account that is sent money.
ANY.
The reason you think other sites don't is they have ALREADY established KYC and AML verification with the customer.
Stripe doesn't want to slow down accounts any more than you do. They (and you) are bound by law and regulation.

Stripe API - Passing a custom parameter does not work for subscription charges

I'm creating a new checkout session and creating a new subscription on that page.
return stripe.checkout.Session.create(
payment_method_types=['card'],
customer_email=payload['email'],
line_items=_get_checkout_products(payload),
mode='subscription',
success_url=host + '?session_id={CHECKOUT_SESSION_ID}&success=true',
cancel_url=host + '?canceled=true',
subscription_data={
"metadata": {
'user_id': get_account_id(event)
}
}
)
Unfortunately it only shows on subscription.created event but not in charges for said subscription for the next period.
Why is it not showing up in the next invoice.payment_succeeded or charge.succeeded?
If anyone is looking for this - currently there's no way to pass parameters reliably between all events.

nlapiSendEmail returns SSS_AUTHOR_MUST_BE_EMPLOYEE from correct employee id (on Sandbox)

in a Sandbox environment nlapiSendEmail (defined inside a suitelet) returns SSS_AUTHOR_MUST_BE_EMPLOYEE even when the sender id is correct
My distribution is Kilimanjaro, with SuiteScript 1.0. I have an administrator role, when calling nlapiSenEmail() directly from the backend model with my employee id, the email was sent to my employee profile, but not to the specified email, which is really a company distribution list. Even when I did not specify the logged customer email, a copy was sent to the logged customer email, a gmail account. The backend model operates only for the MyAccount application. It's worth noting that in this scenario nlapiSendEmail() return value was undefined. In my experience, Netsuite is really ambiguous in its behavior returning values or just functioning in an expected way, due to the "execution context". So, with the same data I put my call inside a suitelet, and now I am having the return SSS_AUTHOR_MUST_BE_EMPLOYEE.
function sendEmailWithAPI(request, response)
{
var senderId = request.getParameter('senderId');
var to = request.getParameter('emailTo');
var subject = request.getParameter('subject');
var body = request.getParameter('body');
var cc = request.getParameter('emailCC');
var result = {success:false, errorInfo:''};
try
{
var sendingResult = nlapiSendEmail(senderId, to, subject, body, cc);
result.success = true;
}
catch (errorOnMailSending)
{
result.returnValue = sendingResult;
result.errorInfo = errorOnMailSending.details;
}
response.write(JSON.stringify(result));
}
What is the record type of the senderId? NetSuite only accepts Employee records as sender of script generated emails. Also in Sandbox accounts, the emails are re-routed to the logged in user, specific list, or not at all. This is actually based on the Company preference in your Sandbox account. The reason for this is Sandbox is usually used for testing and you don't want to send test emails to actual customers.
In the end the "problem" was that I was just working in the Sandbox, as I prepared a snippet to test email sending in production everything went right. In the sandbox you can still send emails specifying a list at the subtab "Email options"
with the option "SEND EMAIL TO (SEPARATE ADDRESSES WITH COMMAS)"
this is located at Setup > Company > Email Preferences.

Stripe Products - Changing plan is not removing the old plan

I recently switched to the latest stripe API where plans have moved into products. Everything is working fine, but changing plans retains the old plan too which was not happening previously.
If I subscribe user to Plan A, then change the plan to Plan B, now instead of showing one plan for the user it is showing two plans.
So if I try to switch to Plan A, it throws an error:
Stripe.StripeException: Cannot add multiple subscription items with the same plan:
Following is my code, which was working fine before the upgrade:
public async Task SetPlan(string subscriptionId, string plan)
{
var subscriptionService = new StripeSubscriptionService();
var items = new List<StripeSubscriptionItemUpdateOption>()
{
new StripeSubscriptionItemUpdateOption() { PlanId = plan }
};
var subscriptionOptions = new StripeSubscriptionUpdateOptions()
{
CancelAtPeriodEnd = false,
Prorate = true,
Items = items
};
await subscriptionService.UpdateAsync(subscriptionId, subscriptionOptions);
}
Your call here is adding a new item/plan though and not replacing the existing one.
To replace one plan by another you need to also pass the current subscription item's id in Id along with the new plan id for this to work.

How do I add a custom form for my collection_action/controller?

I have the following:
collection_action :new, :method => :post do
begin
user = User.find_by_email(params[:email])
if user
UserPermission.create(:user_id => user.id,
:permission => UserPermission::SUPPORT,
:creator => current_user)
end
rescue ActiveRecord::RecordNotFound
flash[:warn] = 'User not found'
end
redirect_to admin_support_users_path, notice: 'Support user added.'
end
form do |f|
f.inputs do
f.input :email
end
end
action_item only: [:index], :method => :post do
link_to 'Add Support User', new_admin_support_user_path
end
The above works in the sense that no error is thrown. The support users page loads and I'm able to click the Add Support User button. However, 'Support user added.' is immediately shown. The Add Support User button does not take me to a form to enter an email. How do I add/create/use a form that passes an email parameter to my collection_action?
I'm new to activeadmin and documentation is sparse, so any help is appreciated. Thanks.
Figured it out. I'll try to explain as I understand it. And my initial question may have been unclear. The reason I was getting the, 'Support user added.' message is because I was updating the wrong method. The method above should have been the :create controller method, not the :new controller method. :new uses HTTP GET, which is why it would go directly to the redirect. :create accepts an HTTP POST. So, instead, I have the following:
def create
begin
user = User.find_by_email(params[:email])
if user
UserPermission.create(:user_id => user.id,
:permission => UserPermission::SUPPORT,
:creator => current_user)
end
rescue ActiveRecord::RecordNotFound
flash[:warn] = 'User not found'
end
redirect_to admin_support_users_path, notice: 'Support user added.'
end
def new
render 'new.html.arb', :layout => 'active_admin'
end
And this correctly creates a nice looking active admin form, accepting an email parameter.
You just need to add another action--just like a normal resource needs separate actions for create and new. Your 'new' action can render a custom form either inline or in a separate partial, as shown here:
http://www.activeadmin.info/docs/5-forms.html
That said, I'm not sure I understand why you need a custom action. Is this in your User resource file in active admin? If so you can just use the default new user action and include the current user in the form as a hidden variable as the creator. If this is not in your User resource active admin file then you probably need one.

Resources