I spent two days on it and I finally got it. I made a small service which does something after the authentication. It’s JavaScript, but I’ll just post the general flow for you.
This assumes you have a server which can handle redirect and registered client and it has APIs selected to V2. If you making some desktop software you must run a localhost webserver at some port to handle the redirect. Make sure to set up redirect URL in Pateron’ Client setting in the Patreon.
So, it’s a 3 step thing. First you open a browser window with similar link
window.open("https://www.patreon.com/oauth2/authorize?client_id=l4YlAvUnfrNrTk9uiupqgnAb3PxDIScWv_FU2f5GVrCiyxEIJCeALuxzLB9R335s&scope=identity%20identity.memberships&allow_signup=false&response_type=code&redirect_uri=https://supporters.solar2d.workers.dev/patreon-callback", "Login with Patreon", 'toolbar=no, menubar=no, width=400, height=600')
This is when you hand stuff to patreon. Window is presented with login and asking for user to provide you information. This is where I stuck for longest, because for some silly reason you must provide both identity
and identity.memberships
scopes. Second one does not assume first one.
Then Flow is goin on your server. After user is done and authorised (or not) Patreon would make a redirec to your callback. This is where your server must handle things.
Server would redirect with the ‘code’ get parameter. You need to exchange code for access token. This is done with a post request
const code = url.searchParams.get("code");
if (!code)
return new Response("No valid authentication code", {
status: 500,
});
const body = {
client_id: patreonClientId,
client_secret: patreonClientSecret,
code: code,
grant_type: "authorization_code",
redirect_uri: "https://supporters.solar2d.workers.dev/patreon-callback",
};
const init = {
body: new URLSearchParams(Object.entries(body)).toString(),
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded",
accept: "application/json",
},
};
const response = await fetch("https://www.patreon.com/api/oauth2/token", init);
const results = await response.json();
if (results.error)
return new Response("Patreon Error: " + (results.error_description || results.error), {
status: 500,
});
Note that exchanging Code for token requires a x-www-form-urlencoded data. I use java script built in facilities for that, but it isn’t hard just to replace part of the string with your code
.
After latest request is finished, you have access token in results.access_token
and you can use it to make API calls.
I’m only want to verify if user is supporting my patreon. I make following request for it:
const ptResponse = await fetch("https://www.patreon.com/api/oauth2/v2/identity?include=memberships.campaign&fields%5Bmember%5D=patron_status", {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${results.access_token}`,
},
});
if(ptResponse.status != 200) return ptResponse;
try{
const ptData = await ptResponse.json();
const sponsorships = ptData.included.filter(e => e.type === 'member').filter(e => e.attributes.patron_status === "active_patron").filter(e => e.relationships.campaign.data.id === patreonCampaignId)
results.patreonOk = sponsorships.length > 0;
} catch {}
Note that I use token in authorization header. memberships.campaign
is undocumented for some reason, but you need it to get campaign IDs (campaign is a creator’s page). Otherwise you’ll just get pledges without knowing what user is supporting (I know, right?).
Results have different fields, but you need included
array, and only entries from it which have type
field set to member
. Then attributes
would contain for once documented Member fields (note that I requested only patron status, but you can request others, just add them to request comma separated).
Here I filter only for active patrons (attributes.patron_status === "active_patron"
). Last step for me is to only filter for my own campaign. Note that campaign is not stored in Member attributes, it is in relationships.campaign.data.id
.
So, if you have entry with relation to your campaign and with active patron status, user gave you some money.
If you need to store token, note that it will expire, and you got to renew it time to time. But this is outside of the scope of my post.
Good luck!
Hope it helps.
P.S. Easiest way to find your campaign ID is to open your page in browser, right click in the middle of the page and select “View page source”. Then search for “campaign/” and you should find a bunch of links similar to campaign/3949580/
. That number is your campaign ID.