Stripe Subscription:Customer Creation - spring-boot

I am working on stripe subscription integration in my Spring Boot application. I am able to successfully redirect user to the checkout page and process the payment. I am working on the no-code application and the business model of my app is to charge the customer for each project created. Each time the user process the payment I am saving the customer-id and subscription-id in the database of the project but in order to subscribe to the same customer for the next project I have to create a new customer in the Stripe account and then the same flow continues. So, is it possible to subscribe the same customer for a new project without creating the customer in the stripe account?

I don't see why not. You just need to pass the existing customer Id when creating a checkout session.
That's what I'm doing:
If a user doesn't have a stripe customer created, I create one and store the customer id against my internal user id (I also send the user id in the customer metadata to stripe, so it's easy to correlate them, as they may use different email addresses for payment).
If a customer already exists for the current user, I just use that one.
I'm using typescript, the concept is the same:
// uid is my internal user id
const customerId = getOrCreateCustomer(uid);
const newSession: StripeType.Checkout.SessionCreateParams = {
customer: customerId,
mode: 'subscription',
payment_method_types: ['card'],
customer_update: {
address: 'auto',
name: 'auto',
shipping: 'auto',
},
subscription_data: {
metadata: { uid }, // store uid in subscription metadata as well
},
line_items: [
{
price: priceId,
quantity: 1,
}
],
success_url: successUrl,
cancel_url: cancelUrl,
};

Related

Way to limit number of records a user can create in Amplify GraphQL API

I have an app where Auth is implemented using Cognito User Pools and API is a GraphQL API implemented using Amplify. In the Schema definitions, is there an easy way to limit the number of records a user can create. For example in the following schema...
type Product #model #auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
description: String
}
I would like to limit the users to a maximum of 100 Products.
One way is via my front-end. When I detect that a user has reached 100 limit, I can just make the UI stop giving them the ability to add more. But if someone were to bypass the UI, they could create more than 100. Hence, I prefer to enforce this limit in the backend.
Is there a way to do this in the Schema definition, or elsewhere in AWS / DynamoDB ?
Thanks!
There isn't a straightforward way to do this that I'm aware of.
Below is how I would solve this.
Create a #key on Product on the owner property, so that you can query by owner.
Overwrite the CreateProduct mutation. In your custom resolver, before creating a new Product, query the Product table byOwner, using the owner id passed in, to count how many already exist.
Here is the documentation: https://docs.amplify.aws/cli/graphql-transformer/resolvers#add-a-custom-geolocation-search-resolver-that-targets-an-elasticsearch-domain-created-by-searchable
I think the easiest solution would be processing the API request in a lambda function that validates the request (product count < 100) before having the script write to the DB. Then you can null out the built-in mutations for the model to prevent unintended access.
Example Schema:
type Mutation {
addProduct(input: ProductAddInput): ProductAddOutput #function(name: "productLambda-${env}")
}
type Product
#model(queries: null, mutations: null, subscriptions: null) /* update these to what you need */
#auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
description: String
}
In Lambda you can pull the username from the event.identity property and that should correlate to the owner field in the db. Since the AWS package is automatically loaded you should be looking at very fast script execution as long as your db indexes are set properly.
For the user product count, I see a couple of options:
A secondary index set up on the owner field so you don't do a ton of
scans
If you have a user table, you could add a field that counts
the products for each user and just update that table any time you
update the product table.

AWS Amplify authorization based on db field value or relationship

I am creating a webapp with Amplify ( GraphQL api ) and Quasar Framework.
Using Amazon Cognito for authentication.
Lets say the db has these entities:
A User who has his own profile where he can manage his own data, and even make it public if he turns the 'public' boolean field to true.
An Organization who have todos etc.
A User can become an Employee of one ( or maybe more ) organization(s) and should be able to manage for example the todos that belong to the organization where he became an employee.
I am stuck at figuring out how to add authorization rules to make this happen.
Owner authorization should be suitable for the user profile, but even there its not clear how to setup a rule that makes the profile public if the user sets the 'public' boolean field setting to true in his profile.
For example:
type Todo #model #searchable #auth(rules: [{allow: owner, operations: [read, create, update, delete]}]) {
id: ID!
Title: String!
Description: String
}
This way if a user logs in he can manage and list his own todos, but how can I allow him to view and manage todos that belong to an organization where he is an employee ( employee would be a join table which connects the user and the organization )?
I have undertaken some research on this issue, and although it is far from complete, I would like to share it.
Despite amplify docs say, that there is possibility to combine multiple autorization types, they don't specify explicitly, that you can't combine them in one request (my be, it's evident for them, but not for novice like me).
When you configure your AppSync GraphQL API with amplify update api, you choose default authorization type. All subsequent requests from your front by await API.graphql() will use this default unless you explicitly specify different type like this await API.graphql(Object.assign(graphqlOperation(listTodos),{authMode: auth_mode})), where auth_mode can take one of next values: "API_KEY", "AWS_IAM", "AMAZON_COGNITO_USER_POOLS" and "OPENID_CONNECT".
There are two "true" public authorization types in Amplify - "API_KEY" and "AWS_IAM". To activate any of them (or both), you should do something like that:
type Todo #model
#auth(rules: [
{ allow: public },
{ allow: public, provider: iam},
])
{
id: ID!
name: String!
description: String
owner: String
editors: [String]
entity: String
}
{ allow: public } stands for "API_KEY", { allow: public, provider: iam} - for "AWS_IAM".
For "API_KEY"to work propery you should configure API KEY (for ex - with amplify update api). For "AWS_IAM" you should configure Cognito and enable unauthenticated identities in Cognito Identity Pool.
After that, any request without prior user sign-in of the form await API.graphql(Object.assign(graphqlOperation(listTodos),{authMode: auth_mode})) with auth_mode=API_KEY or auth_mode=AWS_IAM will succeed (also updateTodo, createTodo and deleteTodo).
In either case you can't implement your workflow with public field, enabled by user, "out of the box", because permission evaluation get accomplished prior to any database info gets available. For ex, IAM authorization uses unauthenticated role policy, generated for you by amplify update api. You can see it in your AWS console in IAM service.
To partially implement your private/public workflow I can suggest to use so called dynamic group authorization(We assume "AMAZON_COGNITO_USER_POOLS" auth mode). In that case you implement "public" group in your Cognito User Pool and make any user member of this group (you can automate this by using post signUp hooks).
Your #auth could be something like that
type Todo #model
#auth(rules: [
{ allow: owner },
{ allow: groups, groupsField: "groupsCanAccess", operations: [read] },
])
{
id: ID!
name: String!
description: String
owner: String
editors: [String]
entity: String
groupsCanAccess: [String]
}
When your user decides that it's time to go public, she request updateTodo with groupsCanAccess set to public. As you can see, this is partial solution because to read todos your "public" user should be registered.
To partially implement your employee-organization workflow I could suggest next approach (We again assume "AMAZON_COGNITO_USER_POOLS"):
type User #model
#auth( rules: [
{ allow: owner },
{ allow: groups, groupsField: "groupsCanAccess", operations: [read] },
{allow: groups, groups: ["Admin"] }
]) {
id: ID!
owner: String! #auth(rules[{allow: groups, groups: ["Admin"] }])
groupsCanAccess:[String]
#auth(rules[{allow: owner},{allow: groups, groups: ["Admin"] }])
todo: [Todo] #connection(keyName: "byUser", fields: ["owner"])
type Todo #model
(queries: null)
#auth ({allow: owner, operations[delete]})
#key [(name: "byUser", fields: ["owner", "description"])
{
id: ID!
name: String!
description: String
owner: String! #auth(rules[{allow: owner}])
}
Here everybody can create, update and read Todo, but read operation is possible only through User (queries: null), so nobody can get particular id to update particular Todo except owner. There is though some little possibility that someone can guess this id and it's drawback of this approach. It's impossible to create Todo without owner (exclamation sign on field), and nobody can create Todo but owner (nobody can alter owner field but owner). Note that operations[delete] is important, because without that nobody can query Todo through User ( {allow: owner} equivalent to {allow: owner, operations[create,update,read,delete]}).
Owner can do everything with User, but she can't create or delete User because she can't alter owner field protected by perfield #auth directive
Admins can create User and set owner to particular user.
Admins and owner can alter groupsCanAccess field. Every element in this array corresponds to some organization. When Admins or owner add some organization, every member of this group gains access to this User and to all of her Todos through this User. Drawback - owner can forbid access granted by Admins. Because Todos aren't protected from update - every group member can alter Todos of particular User.
Operations of adding Cognito User Pool user to group performed by admins and are out of scope of this document. Drawback - only 300 Groups per Cognito Pool.
And last - you of course have option to manually adjust resolvers, automatically generated by Amplify. You may for ex use "AWS_IAM" and organize owner check by analyzing $context.identity.cognitoIdentityId inside your rezolver mapping template. As you know - there is one-to-one correspondence between this Cognito Identity Pool paramener and user from User Pool. It's especially convinient when you need to store public and owner's Todos (there is some unique per Identity Pool cognitoIdentityId corresponding to unauthenticated user).
Have exact the same problem
My hopefully temporarily solution is : Adding a lamdaFunction : myResolver and adding this function to the graqphl-schema. in that function a can do all the filtering thru the databaseClient Class
the schema
type Query {
myqueryresolver(params: String): String #function (name: "myqueryresolver-${env}")
}
Now You define a lamda (In my case I use a generic form ....) in which you perform all the non standard queries.
You call this function :
const dataObj = await API.graphql(
{
query: myqueryresolver,
variables: {"params": JSON.stringify(params)}
}
);
All the public access goes thru this function call.
Additional hint
You can run it locally with "amplify mock" and you can see all the output from your console.log locally - no need to deploy during dev.

Braintree PAyment create customer and save payment method

I am trying to save the card information and create customer at same time. The client app creates a nonce and sends it to my server (nodejs) and I call:
gateway.customer.create({paymentMethodNonce: request.params.nonce})
The customer gets created and I get a customer ID, I save that in the db. But calling
gateway.customer.find(customer_id):`
returns:
Customer {
id: '697983269',
merchantId: 'yzkvrqy9qsctn69d',
firstName: null,
lastName: null,
company: null,
email: null,
phone: null,
fax: null,
website: null,
createdAt: '2017-09-25T00:37:29Z',
updatedAt: '2017-09-25T00:37:29Z',
customFields: '',
creditCards: [],
addresses: [],
paymentMethods: [] }
Which has empty payment method array. I am using the drop in UI that only asks for card number and exp date. This is also a sandbox account.
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact
support.
Hi Mehdi. I took a closer look at your Braintree Sandbox, and while you are in fact creating customer records in your Vault, it appears that there is no nonce value being passed into the customer creation API call, resulting in a customer record containing only a customer ID.
Would you mind logging, and relaying here, the value you get on your server for your request.params.nonce and ensure it contains a valid nonce string from your client?
Don't hesitate to reach out to support if you need further clarity.

Send 2 different types of mails using mailchimp

I have a set of internal users for my project. Admin can activate/deactivate them. I want to send them a mail saying "your account has been deactivated" when their account is deactivated by admin. Similarly they should receive a mail saying "your account has been activated" when admin activates their account. How can I do this?
I am trying by creating 2 separate lists in mailchimp and two separate campaigns. but when I'm writing mailchimps credentials in my development.js with 2 separate list ids and then trying to get it in my javascript file,it is getting undefined (checked by console.log)..
Is there a way to do it by just single campaign/list?
Here's my development.js code of mailchimp credentials:
mailchimp: {
api_key: "***************-***",
list_id1: "*********", //internal users
list_id2: "*********" //internal deactivated users
},
my user.helper.js
const config = require('../../config/environment');
const Mailchimp = require('mailchimp-api-3');
const mailchimp = new Mailchimp(config.mailchimp.api_key);
exports.addToDeactivatedList = function (email, name) {
console.log(mailchimp.list_id1);
mailchimp.members.create(config.mailchimp.list_id1, {
email_address: email,
merge_fields: {
FNAME: name
},
status: 'subscribed'
}).then(user => { }).catch(e => {
console.log("deactivate list me add ho gya");
})
}
exports.addToActivatedList = function (email, name) {
console.log(mailchimp.list_id2);
mailchimp.members.create(config.mailchimp.list_id2, {
email_address: email,
merge_fields: {
FNAME: name
},
status: 'subscribed'
}).then(user => { }).catch(e => {
console.log("activate list me add ho gya");
})
}
and my user.controller.js (selective part only)
var helper = require('./user.helper');
.
.
if(req.body.status != user.status){
(req.body.status == "active") ? helper.addToActivatedList(user.email, user.name) : helper.addToDeactivatedList(user.email, user.name);
}
All the help will be appreciated. THANKS
I'd try to put everyone in the same list, and then create segments based on that list. After that, create a campaign based on that segment.
You could for instance create a custom list attribute that records wether or not an account is activated and create a segment based on that attribute. The campaign should then be based on that segment.
Perhaps also record the date an account has been activated or deactivated by the admin in another custom attribute and use that to check if a user already had an activation/deactivation mail.
MailChimp offers a feature for situations like this called automations. Automations allow you to send individual emails to subscribers when an event is triggered. So instead of creating separate campaigns every time a user is activated or deactivated, you can use just two automations and a single list.
Whether a user is active or not can be tracked with list merge fields. To do this, you'll need to add a new text merge field to your list. Let's name the field label 'Active'. Uncheck the 'Visible' checkbox so the user can't see it, and name your merge field something like 'ACTIVE'. You can use values like yes/no or true/false to identify the users by their active status.
Next, create your automations, one for activated users and one for deactivated users. You can set a trigger to send the email when a list field value is changed. So just make each of your two automations send the emails when the 'Active' list field values change to either 'yes' or 'no'.
Then all you need to do with the API is subscribe users to a single list whenever their accounts are activated or deactivated. Just make sure the new 'ACTIVE' merge field is set to 'yes' or 'no' when you do this, and any addresses already subscribed will be updated with the new value. So your mailchimp.members.create() would look something like this, based on the example from here:
mailchimp.members.create(<list_id>, {
email_address: <user_email>,
merge_fields: {
FNAME: name,
ACTIVE: 'yes' //Or 'no' if being used for deactivated users
},
status: 'subscribed'
})

Discover Google calendar created with service account

I created new Google calendar with API v3 in c# with Service account. I also set ACL rule:
var permission = new AclRule()
{
Scope = new AclRule.ScopeData() { Type = "domain", Value = "mydomain.com" },
Role = "reader",
};
Problem is how can users of domain "nicely" add this calendar to "Other calendars" in calendar.google.com site?
They can add it if they enter calendar id, which is not user friendly, since id is some random string:
4b123456789glvpvasaaaaaaaar4#group.calendar.google.com
. I though I could search by calendar summary. But this is not the case. Only entering complete calendarId adds it to calendar list.

Resources