Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
A document for QA testing and developers to know how to test features in a non-production environment
Loading...
Loading...
Loading...
Get paid to contribute to Open Source!
The Open Collective engineering team is small, and we're always looking for new contributors to our Open Source codebases. Our Bounty program is an opportunity to solve issues that could be neglected otherwise. Contributors who fix these issues will be rewarded financially.
Our bounty program is about creating opportunities for our community to contribute to Open Collective, to make it their own. It also gives us an opportunity to get to know developers who we could potentially work with more in the future. We celebrate making open source contributions more sustainable by paying, but money alone shouldn't be the primary motivation for participation.
We're not able to accept pull requests that aren't completed to a high standard in a reasonable timeframe. Please only pick up bounties that you are confident you can complete at your current knowledge and skill level. We will not accept pull requests or pay bounties for code that's not up to the standard we need to maintain for the Collectives who rely on this platform.
Our dev team is happy to answer questions and provide some limited support, but we don't have capacity to mentor junior developers through the bounty program.
For general guidelines about what's expected in the code, see more info here.
For reference, until July 2020, we used the following model:
$100: minimal or unknown complexity
$200: simple complexity
$500: medium complexity
Note: we are not able to pay bounties to people based in countries sanctioned by the United States, or countries where US sanctions are so widespread that our payment processors no longer serve them.
But since then we have moved to a more flexible one. Based on the importance of the issue and its complexity, we attach a bounty between $100 and $1000 to the ticket.
We want to attract quality contributions. The issue will only be considered complete and approved for payment if the Pull Request is merged by an Open Collective Core Developer.
Search for issues with attached bounties:
Express interest by commenting on the issue and ask to be assigned
Open a Pull Request and ask for feedback and review
Incorporate feedback from Core Developers, if applicable
PR is reviewed, approved, and merged by a Core Developer
Get paid:
If you can issue an invoice, submit it as expense to Ofitech.
If you can't issue an invoice, ask to be rewarded with an Open Collective Gift Card
See more info about getting paid through Open Collective
Issues tagged as "bounty candidate" are issues that we are considering to add bounties for, but that are not bounties yet - either because they lack proper specifications, a team consensus, or because we don't have the bandwidth to review it at the moment. Feel free to comment on such issues to ask for a bounty to be added if it's something you're willing to work on.
Financial compensation can only happen if the issue has a "bounty" tag with a pre-defined amount. In other words, completing a bounty candidate that didn't receive the "bounty" tag will not make you eligible for the bounty program.
The options supported for paying bounties are the ones supported by Ofitech, namely:
PayPal
Bank account transfers (to countries not in the US sanctioned list)
Make sure the issue is understandable for newcomers and expectations are clearly set
Tag issues with the "bounty" label and amount (e.g. "$100")
Add a comment with a basic explanation of the Bounty process and link to BOUNTY.md
A $100 bounty was attached to this issue. Anyone submitting a Pull Request will be rewarded with $100 when the Pull Request is reviewed, accepted and merged. More info.
Make sure the issue is understandable for newcomers and expectations are clearly set
First Time Contributors
Not part of the Open Collective GitHub organization
Fork our projects on GitHub and push changes on their forks
Have access to minimal or simple complexity issues
Should comment on bounty issues to get assigned (limited to one at a time)
Contributors (at least 1 completed issue)
Added to the "Contributors" group in the Open Collective GitHub organization
Can push branches to the Open Collective repositories
Have access to minimal, simple or medium complexity issues
Can assign themselves bounty issues (limited to one at a time)
Recurring Contributors (3 or more completed issues)
Added to the "Recurring Contributors" group in the Open Collective GitHub organization
Can assign themselves two bounty issues at a time
Confirmed Contributors (3 or more completed issues including at least 2 with medium complexity)
Added to the "Confirmed Contributors" group on the Open Collective GitHub organization
Become candidates to work on complex issues or projects on a negotiable per-project or hourly rate
In Open Collective, every entity is a collective and can be accessed publicly via their unique slug https://opencollective.com/:slug
. In our public API, Collectives are usually refered to as Accounts
. You can think about it like "profiles" that's what we really store in that table.
A Collective can be of type:
COLLECTIVE e.g.
EVENT e.g.
ORGANIZATION e.g. , , etc.
USER e.g.
PROJECT
FUND
A Member connects two profiles together. It can have multiple roles (one role per row):
HOST legal holder of the bank account that holds the money on behalf of the collective
ADMIN users who can approve expenses for the collective
MEMBER aka core contributors
BACKER users who gave money to the collective
An Order is the intent to give money to an Account. It is created by a UserId on behalf of a collective (which can be their own UserCollective or any other Collective that they are a member of).
Attributes:
Note: The Collective currency might be different than the Host Currency (both for the source "From" and the recipient "To"). The fxrate only takes into account the conversion between ToCollectiveCurrency
to ToHostId
.
Documentation for coders contributing to and using the Open Collective software application.
You can contact us on if you have any question.
Describes what we expect from new code. Also includes special tips to help you getting started!
When adding new dependencies, we use .
Don't commit package-lock.json
if you're not making any changes to the libraries.
If the issue you're working on requires changes in both API and Frontend, give your Git branches the same name. CI will automatically pull the correct API's branch when testing the Frontend.
We love screenshots - or better: screencasts. Include some in your pull requests to demonstrate your changes and you will have our eternal gratitude.
I18n
The strings must be internationalized. See .
Update the language files npm run build:langs
and commit them to reflect the changes.
Libraries
Whenever it's possible, we must use styled-components
to write styles. See
We're getting rid of material-ui
. We still rely on this library for some stuff but please don't use it directly.
Icons must be imported from the library.
Testing
Tests written with Cypress must follow our conventions.
A Transaction records money moving from one account to another ac. In this example, a collective is giving €100 to hosted by .
Attribute | Definition | Example |
UserCollectiveId | User who created the order | /xdamman |
FromCollectiveId | Source of the money | /digitalocean |
CollectiveId | Destination of the money | /webpack |
currency | currency of the ToCollectiveId | USD |
amount | amount in cents | 10000 |
SubscriptionId | References recurring subscription |
status | status of the order | PENDING -> APPROVED|REJECTED -> PAID |
Attribute | Definition | Example |
OrderId | References the order | 1 |
FromCollectiveId | Source of the money (virtual account) | /webpack |
ToCollectiveId | Destination of the money (virtual account) | /wwcodeberlin |
PaymentMethodId | Payment method (e.g. if there wasn't enough funds in the |
|
FromHostId | Source of the money | /opensource |
ToHostId | Destination of the money | /wwcode |
FromHostAmount | total amount in cents paid by the host of the | -11481 (-$114.81) |
FromCollectiveAmount | total amount that increases/decreases the balance of the | -11481 |
paymentProcessorFeesInHostCurrency | fees for the payment processor in cents |
hostFeesInHostCurrency | fees for the host in cents in the currency of the host (which might be different than the currency of the collective, e.g. WWCode (USD) and WWCode Berlin (EUR) | 574 (5% of €100 in USD) |
platformFeesInHostCurrency | fees for the platform (Open Collective) | 574 (5% of €100 in USD) |
ToHostAmount | net amount in cents received by the host of the | 9630 (€100 - (2.9% + $0.30) - €5 platform fee) |
ToCollectiveAmount | total amount that increases/decreases the balance of the | 9580 (€96.30 - €5 host fee) |
FromHostCurrency | currency of the | USD |
FromCollectiveCurrency | currency of the | USD |
ToCollectiveCurrency | currency of the collective that receives the money | EUR |
ToHostCurrency | currency of the order (currency of the | USD |
fxrate | Foreign eXchange Rate from the currency of the order ( | 1.15 |
Get detailed information about a collective:
/:collectiveSlug.:format(json|csv)
E.g.: https://opencollective.com/webpack.json
Notes:
image
is the logo of the collective
all amounts are in the smaller unit of the currency (cents)
backersCount
includes both individual backers and organizations (sponsors)
yearlyIncome
is the projection of the annual budget based on previous donations and monthly pledges
Returns all members of the collectives (core contributors, contributors, backers, sponsors)
/:collectiveSlug/members.:format(json|csv)
You can also filter by member type (USER
or ORGANIZATION
):
/:collectiveSlug/members/:memberType(all|users|organizations).:format(json|csv)
E.g.
Parameters:
limit: number of members to return per call
offset: number of members to skip (for pagination)
Notes:
github
is verified via oauth but twitter
is not
email
returns null unless you make an authenticated call using the accessToken
of one of the admins of the collective
all amounts are in the smaller unit of the currency (cents)
type
can be USER
, ORGANIZATION
or COLLECTIVE
role
can be ADMIN
, MEMBER
, BACKER
, ATTENDEE
, FOLLOWER
tier
is the name of the tier
isActive
specifies if the backer has an active subscription
/:collectiveSlug/[all|users|organizations].:format(json|csv)?TierId=:TierId
You can find the TierId
by looking at the URL after clicking on a Tier Card on the collective page (e.g. TierId
for https://opencollective.com/webpack/order/266 is 266
).
Alternatively, you can also use the slug of a tier:
/:collectiveSlug/tiers/:tierSlug/[all|users|organizations].format(json|csv)
E.g.
/v1/collectives/:collectiveSlug/transactions?type=:type&limit=:limit&offset=:offset&dateFrom=:dateFrom&dateTo=:dateTo&type=:includeVirtualCards
Return All Transactions of a collective given its slug.
limit: number of members to return per call
offset: number of members to skip (for pagination)
type: filter transactions of type DEBIT
or CREDIT
dateFrom: the start date (format YYYY-MM-DD
) to be considered when returning the data
dateTo: the end date (format YYYY-MM-DD
) to be considered when returning the data
includeVirtualCards: a boolean that, if true, will include the transactions generated by all virtual cards issued by the specified collective
PS: For more details on how to have a Client ID/API Key, get in touch.
E.g.
Including Virtual Card transactions (transactions that used a virtual card that was issued by the collective): https://api.opencollective.com/v1/collectives/opencollectiveinc/transactions?api_key=YOUR_API_KEY&includeVirtualCards=true
NOT Including Virtual Cards: https://api.opencollective.com/v1/collectives/opencollectiveinc/transactions?api_key=YOUR_API_KEY
Using limit=20
, type=DEBIT
and offset=5
: https://api.opencollective.com/v1/collectives/opencollectiveinc/transactions?api_key=YOUR_API_KEY&includeVirtualCards=true&limit=20&type=DEBIT&offset=5
The output will be a json with a result property that will contain an array. here is an example:
We have a UI for exporting CSV or JSON data if you prefer.
GET https://opencollective.com/:collective/members/[all|users|organizations].json
E.g. https://opencollective.com/webpack/members/organizations.json
Technical details about how we implement TransferWise and how to get started developing with it.
TransferWise integration can be used to automate expense payment as a way to provide one-click wire transfer for expenses.
In order to create a new TRANSFERWISE_PRIVATE_KEY
you'll need to generate a new key pair:
After that, you can encode the private key using base64 and save it as an environment variable:
Sign up for a developer account sandbox.
Two-factor authentication (2FA) code for sandbox login is 111111.
Add a new Token.
Done, now you can copy your token!
Manually create a ConnectedAccount with generated clientId
and clientSecret
:
We're currently defaulting to your Business profile if two profiles exists. If you have two profiles and want to use your personal one, make sure to add { "type": "personal" }
to the data
column of the created Conencted Account.
The future of the Open Collective API is our public GraphQL API. You can check the documentation on https://graphql-docs-v2.opencollective.com.
Learn more about it here: https://medium.com/open-collective/open-collective-graphql-api-preview-3b42ed1d55ff
Our REST API is still supported but we're not working on it anymore.
Get list of members
Get collective info
Get members
Get members per tier
Get transactions from collective
The PDF service supports passing either a Personal-Token
(for personal tokens) or an Authorization
header (for OAuth).
Getting the receipt for a contribution
Getting the PDF for an Invoice Expense
Getting a bundled receipt for a fiscal host/period
To manually run the monthly report:
1- Update the template: https://github.com/opencollective/opencollective-api/blob/main/templates/emails/collective.monthlyreport.hbs
2- If you want to preview some of the emails, you can run it locally with
(it won't send real emails but it will output links to preview them in the browser)
3- Run bash on heroku production:
4- Run the monthly cron job manually:
If you want to only run it for certain collectives, modify the query
here: https://github.com/opencollective/opencollective-api/blob/main/cron/monthly/collective-report.js
A document for QA testing and developers about testing conversations a non-production environment
Prerequisite : Enable conversations
For newly created collectives, conversations are enabled by default!
Otherwise go to the settings page (/{collective}/admin
), click on Conversations and enable
Go to /{collective}/conversations/new
Go to /{collective}/conversations
Documenting how we handle translations in the code
We use to manage our translations. They are extracted from the code to src/lang/${locale}.json
files using the npm run build:langs
command (CI will notify you if the translation files are outdated). Don't translate the strings directly in the files, we use to manage our translatations.
Example
defaultMessage
string breakdown:
action
variable name
select
keyword
delete
and archive
possible values
other
all other values will use this key
An exception to this rule: very common enums or the ones with many possible values should be implemented as a separate file listing all values because:
Re-usability
A map of translations is easier to read than a long select string with tons of options
The order of the words may change from a language to another. For this reason we must always pass the values to be replaced in values
so their order can later be changed.
Example
Splitting a string is alway problematic, because translators loose the context: the strings may not be next to each others when they'll be translated.
In many Latin languages, the translation for a string like "Created at" will depend on the context because of the feminine/masculine forms. The best way to provide this context is to set an ID on the string:
In some parts of the code we translate links like this:
This is bad because we're creating two strings and translators loose the context when they translate one. You should do this instead:
The FormattedMessage
component is the main way to translate strings. To use it, you just need to add the following import:
Then you just add the component with an unique id
and a defaultMessage
.
For VSCode users, you can use the following snippet to make your life easier:
To get help generating an integration like or , follow these steps:
Install to make it easy to compare PRs in the last step
Install opencollective-setup
Get a personal token from . Check all the repo
related permissions.
Create a file in your home directory (on Mac OS X or Linux) called .opencollective.json
and add token in it:
Run cli for a given repo:
Ex: To integrate with MochaJS (), run:
-i
makes it interactive.
Answer questions asked by the script - usually defaults are good to go with. Verify that the slug of the project is the same as the one in the database (script guesses at it and is usually right).
Script attempts to do several integrations across README.md, CONTRIBUTORS.md and ISSUE_TEMPLATE.md. The most important ones are the two integrations on README.md: backers and sponsor badges at the top and adding backer/sponsor section near the bottom.
You can create a custom URL to donate a specific amount (and frequency) by appending /donate
, /pay
or /contribute
to your collective url: e.g.
You can also append to that url a redirect parameter. That way, after the user donates money, they will be redirected to your URL and we will pass the transactionid
.
E.g.
When you donate you will be redirected to:
Then you can call our API to get all the details about that transaction:
You can get your API key in your "Applications" page that you can access from your logged in user dropdown menu.
Example of the data being returned:
We use Cypress for our end-to-end tests. This page references our custom commands and the best practices that we try to follow.
We should try to stick with as much as possible. In short, we should currently enforce:
Don’t target elements based on CSS attributes such as: id, class, tag Add data-* attributes to make it easy to target elements
Tests should not rely on each others results and should be repeatable (we must be able to run it multiple times consecutively). You can use commands like cy.signup()
to ensure that you start from a fresh context.
To improve the testing experience and the readability of our tests, we have defined a set of custom commands in cypress/support/commands.js
.
Both the cy.login
and cy.signup
commands accept a redirect parameter. You can use it to be redirected to a specific page as soon as the command succeed.
cy.login({email, redirect} = params)
Login with an existing account. If not provided in params
, the email used for authentication will be testuser+admin@opencollective.com
.
Note that addresses formatted like test*@opencollective.com
are a special case that let you login directly without the need to confirm your email. You can use the randomEmail
helper to generate these.
cy.signup({user, redirect} = params)
Create an account and login with it. If no params is provided, the account will be created with a random email.
cy.createCollective({ type
= 'ORGANIZATION', email =
defaultTestUserEmail})
Helper to quickly create a collective that use designated by email
will be an admin of.
cy.addCreditCardToCollective({ collectiveSlug })
Adds a default test credit card to the collective referenced by collectiveSlug
cy.fillStripeInput(container, cardParams)
Fills a stripe input.
container
(optional) the DOM that contains the input
cardParams
(optional) the credit card info. Defaults to a valid card. Keys:
creditCardNumber
expirationDate
cvcCode
postalCode
cy.openEmail(filterFunc)
This function is used to open an email in Cypress. If the command succeed, a page with the email is loaded and you'll be able to run all the usual cypress commands (cy.get
, cy.contains
...) to test it.
Examples
cy.getInbox()
cy.clearInbox()
Clears the inbox. It is a good practice to run it in before
to ensure that your test cannot be influenced by the emails sent in previous tests.
When connecting your Open Collective account to GitHub you will be asked to grant these permissions:
Code
Issues
Pull Requests
Wikis
Settings
Webhooks services
Deploy Keys
We recognize that this is asking for a lot of permissions. We looked into it thoroughly, and unfortunately we need all of these permissions for Open Collective functionality to work.
For more details, see the .
Technical details about how we implement PayPal and how to get started developing with it.
This is the account you'll use to make the (fake) payment. Go to , login with your personnal PayPal account then create a test account.
Create an app here:
Use the generated merchant credentials to set the following variables in API's .env
:
3. Encrypt your client secret, from the API repository:
4. Manually create a ConnectedAccount with your clientId
and your encrypted clientSecret
:
5. Create buyer's credentials on
And you're ready to go. Use the credentials generated in step 2. to authenticate when ordering.
How we deal with collective's addresses and countries
On the GraphQL side things are easy, you can just fetch collective.location
and you'll get an object like:
On the database side things are organized this way:
geoLocationLatLong
=> coordinates (eg. POINT (43.6515899 -70.29052239999999)
)
countryISO
=> two letters country code (eg. FR
, BE
...etc)
address
=> postal address normally without country (eg. 12 opensource avenue, 7500 Paris
)
locationName
=> a name for the location (eg. Google Headquarters
)
How to setup Virtual Cards in local development
This documentation is about development. To setup a production environment, see
On staging, is already configured to issue virtual cards. If what you're working on doesn't require any API changes, feeel free to use it directly.
The steps to configure Stripe issuing on your local setup are:
Ask Stripe to enable Issuing on your account https://dashboard.stripe.com/setup/issuing/activate (if you're managing multiple accounts, make sure to pick the right account on top)
Top Up the Issuing balance with a reasonable amount ($1000 ?, $10,000 ?)
Create a generic Card Holder and make sure it's the only one (if you have more, let us know)
Configure webhook and enable for all issuing_authorization.*
and issuing_transaction.*
(5 events) https://api.opencollective.com/webhook/stripe
Configure default authorization process and webhook (deny or allow) (Optional) https://dashboard.stripe.com/settings/issuing/authorizations
Create new dedicated Restricted Secret Key, select write
for all Issuing features (Name: Restricted Issuing)
Contact Open Collective Engineering team to configure Restricted Secret Key production (update-connected-account-stripe-token)
Contact Open Collective Engineering team to configure Webhook Signing secret to connected account (stripeEndpointSecret)
Ask Open Collective Engineering team to enable feature in Collective settings (privacyVcc)
Privacy is deprecated and shouldn't be used for new developments
3. Connect to the database with your favorite tool (psql, DBeaver, Postico, etc.) and search for the host you want to enable Privacy for. Edit its settings
to set features.privacyVcc
to true
4. Open the host settings and go to the "Fiscal host settings" > "Sending Money" section
5. Paste your API Key in the "API Key" field and click on "Connect Privacy".
/:collectiveSlug/events.json
E.g.
Parameters:
limit: number of events to return
offset: number of events to skip (for pagination)
Notes:
url
is the url of the page of the event on opencollective.com
info
is the url to get the detailed information about the event in json
/:collectiveSlug/events/:eventSlug.json
Notes:
url
is the url of the page of the event on opencollective.com
attendees
is the url to get the list of attendees in JSON
We're happy to have you contributing to our codebase! We recommend you go through the following guide.
You'll need to have some basic programming experience with the technologies and tools we use.
Git & Github - Clone, commit and open a PR using Git with GitHub. Check out the following tutorials:
JavaScript/Node.js - We recommend having basic experience working with Node, which Open Collective is written in (frontend & backend). Check out these free JavaScript & Node tutorials:
Javascript
Node.js
GraphQL - Our API uses GraphQL, powered by and . Understanding how this work is important to contributing to or fixing the majority of the issues on our API. To learn more, check out these tutorials & articles:
React & Next.js - You'll need to understand React and Next.js to contribute to issues on the frontend. Check out the following links:
React
Next.js
The project's core repositories are divided into three:
This section explains how you can get Open Collective running locally on your computer.
Setting up the frontend is straightforward. We've provided a comprehensive guide in a separate document that explains how to set up the project.
NOTE: If you're only contributing frontend code, you don't need to setup the API.
Just like the front end, we have a separate document for the setup.
Before you make your first commit, read through our commit convention, provided in the link below:
We recommend you learn more about our bounty program through the link below:
If you are stuck or have a question, join our Discord #engineering channel through the link below:
We're trying our best to make our documentation better. We encourage you to give suggestions on how we can improve it.
See as an example.
Just go to , click on Target languages
pick the language and click Update
.
To activate a language on the website, we usually wait to have a correct translated ratio (20-30%). Then activate it by adding a new line in .
fiterFunc
is a function used to filter the list of emails. As soon as it returns true
, command will start to open the email. For a complete list of the fields you can use to filter the emails, see
Return the full inbox as a list of . cy.openEmail
should be privileged, but this one can be useful if you need to do more advanced verification like counting the number of emails or who the email was sent to.
The button may require multiple clicks to trigger on dev or staging. It should not affect production (see )
Create an account on
Go to , scroll down to "Enable API", toggle the switch, click on Sandbox and copy the Sandbox API key (NOT THE PRODUCTION ONE)
E.g.
- Here is where we manage issues and community discussions. Our issues are all labelled with a complexity label. We recommend starting with simple issues ( ).
- This repository contains our frontend code. You can find more information in the setup section of this guide.
- This contains our API code. If you enjoy working on the backend, you can set up our API locally. To learn about setting it up, check out the setup section below.
The API setup requires more effort than the frontend, as it requires installing the . You might experience difficulty setting up the API on a Windows environment. We recommend using a Unix environment.
Like to contribute to our design? Checkout our .