Help us understand the problem. What is going on with this article?

個人開発のサービスを売買できる「個人開発のフリマ」を作りました

title.png

個人開発のフリマは、個人開発やスタートアップで開発したプロダクトをフリマ感覚で売買できるサービスです。

以前、個人開発者の知見を集めたい!「開発会議」を作りましたを投稿させていただき、200名以上の個人開発者の方々にご登録いただき、より良いサービスづくりをしていく為にインタビューを始めました。そのお話をお伺いする中で、自分のサービスで食べていく以外に「サービスの売却」という選択肢を視野に入れていることが分かりました。

また、私自身も以前、開発してバズったサービスを売却しようと考えた時がありました。しかし、従来のWebメディア売買を主要取引としたサイト群では審査に落ちてしまい、結果的に商機を逃してしまったのかなと思っています。

何ができるの

ズバリ、売ったり買ったりです。

開発環境

  • Rails
  • Heroku
  • CloudFlare
  • Amazon s3
  • Amazon SES

売買スキームの実装

今回の売買では決済は実装していません。いずれ実装するかもしれませんが、まずは銀行口座によるマニュアルの振込も想定しています。交渉の申し込みから、契約内容の締結までを説明していきたいと思います。

交渉に関連するモデル

models/user.rb
class User < ApplicationRecord
  has_many :businesses, dependent: :destroy

  has_many :buy_negotiations,  class_name: "Negotiation",
                                  foreign_key: "buyer_id",
                                  dependent: :destroy
  has_many :sell_negotiations, class_name: "Negotiation",
                                  foreign_key: "seller_id",
                                  dependent:   :destroy
  has_many :buy_content, through: :buy_negotiations,  source: :buyer
  has_many :sell_content, through: :sell_negotiations, source: :seller
  has_many :messages
end
models/business.rb
class Business < ApplicationRecord
  belongs_to :user
  has_many :negotiations
end
models/negotiation.rb
class Negotiation < ApplicationRecord
  before_create :set_slug

  belongs_to :business
  has_many :messages

  belongs_to :buyer, class_name: "User"
  belongs_to :seller, class_name: "User"

  validates :buyer_id, presence: true
  validates :seller_id, presence: true

  def to_param
    slug
  end

  private

  def set_slug
    loop do
      self.slug = SecureRandom.uuid
      break unless Negotiation.where(slug: slug).exists?
    end
  end
end
models/message.rb
class Message < ApplicationRecord
  belongs_to :user
  belongs_to :negotiation
end

交渉(negotiation)は、URLを念の為スラッグで作るようにしています。

1) 交渉の申し込み

frima.png

views/businesses/show.html.erb
<%= form_with model: @negotiation, url: negotiations_path, local: true do |f| %>
  <%= f.hidden_field :business_id, value: @business.id %>
  <%= f.hidden_field :seller_id, value: @business.user.id %>
  <%= f.hidden_field :buyer_id, value: current_user.id %>
  <%= f.submit "売却の交渉を始める", class: "btn btnPrimary btnStyle" %>
<% end %>
controllers/businesses_controller.rb
  def show
・・・
    @negotiation = @business.negotiations.build
  end

Businessに紐づいた、交渉(negotiation)を作っていきます。

controllers/negotiations_controller.rb
  def create
    @negotiation = Negotiation.create(create_params)
    if @negotiation.save
      NegotiationMailer.send_notification_to_seller(@negotiation).deliver
      flash[:success] = "交渉が始まりました!"
      redirect_to @negotiation
    else
      flash[:alert] = "交渉の開始に失敗しました。"
      redirect_to root_path
    end
  end

交渉が始まったら、売り手側にメールが飛ぶように設定しています。

2) 交渉

frimafrima.png

views/negotiations/show.html.erb
・・・
    <div class="chatBox">
      <%= render partial: 'messages/form', locals: { message: @message, negotiation: @negotiation, negotiation_id: @negotiation.id } %>
    </div>
    <div id="comments_area">
       <%= render partial: 'messages/index', locals: { messages: @messages } %>
    </div>
・・・
views/messages/_form.html.erb
<%= form_with(model: [negotiation, message] ) do |form| %>
  <div class="chatInput">
    <%= form.text_area :content, rows: "3" %>
  </div>
  <div class="chatHead">
    <%= form.submit "送信する" %>
  </div>
<% end %>

views/messages/_index.html.erb
<ul>
  <% messages.each do |message| %>
    <% unless message.id.nil? %>
      <li>
        <div class="msg">
          <div class="flexContainer">
            <div class="icon">
              <%= image_tag message.user.image.to_s %>
            </div>
            <div class="text">
              <div class="name">
                <span><%= message.user.username %></span>
              </div>
              <div class="content">
                <p><%= message.content %></p>
              </div>
            </div>
          </div>
        </div>
      </li>
    <% end %>
  <% end %>
</ul>
views/messages/index.js.erb
$("#comments_area").html("<%= j(render 'index', { messages: @message.negotiation.messages }) %>")
$("textarea").val('')

メッセージのやり取りはリアルタイム性を意識して、Firebaseなどの導入も考えたのですが、最初は実用最低限でいいかなと思って、普通のJSでやりました。

controllers/negotiations_controller.rb
  def show
    @negotiation = Negotiation.find_by_slug(params[:slug])
    @message = Message.new
    @messages = @negotiation.messages
  end
controllers/messages_controller.rb
  def create
    @message = @negotiation.messages.build(create_params)
    @message.user_id = current_user.id
    if @message.save
      MessageMailer.send_notification_to_receiver(@message).deliver
      render :index
    end
  end

negotiationの綴りを何回かタイポして、エラーが起こりました。
モデル名はもっと、なるべくシンプルな方がいいですね。

サービスの使い方はCrieitさんに投稿したのでこちらを参考にしてください。

いずれ、誰かが作るサービスだなと思っていましたが、であれば気づいた自分がまずトライすべきだなと思って、見切りでリリースしていました。すぐに売れることはなくても、とりあえず登録しておけば、忘れた頃に売れるかもしれません!

まずは、自分が作ったサービスに「値付け」から始めてみませんか?

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした